Merge "dual-mode switching single/multiuser ServiceWatcher"

This commit is contained in:
Victoria Lease
2013-02-05 17:38:48 +00:00
committed by Android (Google) Code Review
12 changed files with 132 additions and 72 deletions

View File

@@ -23,6 +23,5 @@ LOCAL_JAVA_LIBRARIES := com.android.location.provider
LOCAL_PACKAGE_NAME := FusedLocation
LOCAL_CERTIFICATE := platform
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)

View File

@@ -18,14 +18,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.location.fused"
coreApp="true">
coreApp="true"
android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<application
android:label="@string/app_label">
android:label="@string/app_label"
android:process="system">
<uses-library android:name="com.android.location.provider" />
@@ -39,6 +42,7 @@
<action android:name="com.android.location.service.FusedLocationProvider" />
</intent-filter>
<meta-data android:name="serviceVersion" android:value="0" />
<meta-data android:name="serviceIsMultiuser" android:value="true" />
</service>
</application>
</manifest>

View File

@@ -24,13 +24,17 @@ import com.android.location.provider.LocationProviderBase;
import com.android.location.provider.ProviderPropertiesUnbundled;
import com.android.location.provider.ProviderRequestUnbundled;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.WorkSource;
public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
@@ -60,6 +64,19 @@ public class FusedLocationProvider extends LocationProviderBase implements Fusio
super(TAG, PROPERTIES);
mContext = context;
mEngine = new FusionEngine(context, Looper.myLooper());
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mEngine.switchUser();
}
}
}, UserHandle.ALL, intentFilter, null, mHandler);
}
/**

View File

@@ -300,4 +300,12 @@ public class FusionEngine implements LocationListener {
s.append(" ").append(mStats.get(NETWORK)).append('\n');
pw.append(s);
}
/** Called on mLooper thread */
public void switchUser() {
// reset state to prevent location data leakage
mFusedLocation = null;
mGpsLocation = null;
mNetworkLocation = null;
}
}

View File

@@ -365,7 +365,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext,
LocationManager.NETWORK_PROVIDER,
NETWORK_LOCATION_SERVICE_ACTION,
providerPackageNames, mLocationHandler, mCurrentUserId);
providerPackageNames, mLocationHandler);
if (networkProvider != null) {
mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
mProxyProviders.add(networkProvider);
@@ -379,7 +379,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext,
LocationManager.FUSED_PROVIDER,
FUSED_LOCATION_SERVICE_ACTION,
providerPackageNames, mLocationHandler, mCurrentUserId);
providerPackageNames, mLocationHandler);
if (fusedLocationProvider != null) {
addProviderLocked(fusedLocationProvider);
mProxyProviders.add(fusedLocationProvider);
@@ -392,7 +392,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// bind to geocoder provider
mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames,
mLocationHandler, mCurrentUserId);
mLocationHandler);
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
@@ -404,11 +404,11 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
private void switchUser(int userId) {
mBlacklist.switchUser(userId);
mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
synchronized (mLock) {
mLastLocation.clear();
for (LocationProviderInterface p : mProviders) {
updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
p.switchUser(userId);
}
mCurrentUserId = userId;
updateProvidersLocked();
@@ -668,8 +668,17 @@ public class LocationManagerService extends ILocationManager.Stub {
}
private boolean isAllowedBySettingsLocked(String provider, int userId) {
if (userId != mCurrentUserId) {
/**
* Returns "true" if access to the specified location provider is allowed by the current 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
* @return
*/
private boolean isAllowedBySettingsLocked(String provider, int uid) {
if (UserHandle.getUserId(uid) != mCurrentUserId && !isUidALocationProvider(uid)) {
return false;
}
if (mEnabledProviders.contains(provider)) {
@@ -862,7 +871,7 @@ public class LocationManagerService extends ILocationManager.Stub {
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
ArrayList<String> out;
int callingUserId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();;
long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -873,7 +882,7 @@ public class LocationManagerService extends ILocationManager.Stub {
continue;
}
if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
if (enabledOnly && !isAllowedBySettingsLocked(name, uid)) {
continue;
}
if (criteria != null && !LocationProvider.propertiesMeetCriteria(
@@ -949,7 +958,8 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationProviderInterface p = mProviders.get(i);
boolean isEnabled = p.isEnabled();
String name = p.getName();
boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId);
boolean shouldBeEnabled = isAllowedBySettingsLocked(name,
UserHandle.getUid(mCurrentUserId, 0));
if (isEnabled && !shouldBeEnabled) {
updateProviderListenersLocked(name, false, mCurrentUserId);
changesMade = true;
@@ -1260,7 +1270,7 @@ public class LocationManagerService extends ILocationManager.Stub {
oldRecord.disposeLocked(false);
}
boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
boolean isProviderEnabled = isAllowedBySettingsLocked(name, uid);
if (isProviderEnabled) {
applyRequirementsLocked(name);
} else {
@@ -1317,7 +1327,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// update provider
for (String provider : providers) {
// If provider is already disabled, don't need to do anything
if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) {
if (!isAllowedBySettingsLocked(provider, UserHandle.getUid(mCurrentUserId, 0))) {
continue;
}
@@ -1358,7 +1368,7 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationProviderInterface provider = mProvidersByName.get(name);
if (provider == null) return null;
if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null;
if (!isAllowedBySettingsLocked(name, uid)) return null;
Location location = mLastLocation.get(name);
if (location == null) {
@@ -1538,13 +1548,32 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return false;
return isAllowedBySettingsLocked(provider, mCurrentUserId);
return isAllowedBySettingsLocked(provider, UserHandle.getUid(mCurrentUserId, 0));
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* 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 (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return true;
}
for (LocationProviderProxy proxy : mProxyProviders) {
if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return true;
}
return false;
}
private void checkCallerIsProvider() {
if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
== PackageManager.PERMISSION_GRANTED) {
@@ -1558,14 +1587,10 @@ public class LocationManagerService extends ILocationManager.Stub {
// also allow providers with a UID matching the
// currently bound package name
int uid = Binder.getCallingUid();
if (isUidALocationProvider(Binder.getCallingUid())) {
return;
}
if (mGeocodeProvider != null) {
if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
}
for (LocationProviderProxy proxy : mProxyProviders) {
if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
}
throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
"or UID of a currently bound location provider");
}
@@ -1817,7 +1842,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
synchronized (mLock) {
if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
if (isAllowedBySettingsLocked(provider, UserHandle.getUid(mCurrentUserId, 0))) {
handleLocationChangedLocked(myLocation, passive);
}
}

View File

@@ -16,9 +16,11 @@
package com.android.server;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -44,6 +46,7 @@ import java.util.List;
public class ServiceWatcher implements ServiceConnection {
private static final boolean D = false;
public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
private final String mTag;
private final Context mContext;
@@ -59,7 +62,11 @@ public class ServiceWatcher implements ServiceConnection {
private IBinder mBinder; // connected service
private String mPackageName; // current best package
private int mVersion = Integer.MIN_VALUE; // current best version
private int mCurrentUserId;
/**
* Whether the currently-connected service is multiuser-aware. This can change at run-time
* when switching from one version of a service to another.
*/
private boolean mIsMultiuser = false;
public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
List<String> initialPackageNames) {
@@ -80,15 +87,13 @@ public class ServiceWatcher implements ServiceConnection {
}
public ServiceWatcher(Context context, String logTag, String action,
List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
mContext = context;
mTag = logTag;
mAction = action;
mPm = mContext.getPackageManager();
mNewServiceWork = newServiceWork;
mHandler = handler;
mCurrentUserId = userId;
mSignatureSets = getSignatureSets(context, initialPackageNames);
}
@@ -97,7 +102,22 @@ public class ServiceWatcher implements ServiceConnection {
if (!bindBestPackageLocked(null)) return false;
}
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
switchUser();
}
}
}, UserHandle.ALL, intentFilter, null, mHandler);
// listen for relevant package changes
mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
return true;
}
@@ -114,9 +134,10 @@ public class ServiceWatcher implements ServiceConnection {
intent.setPackage(justCheckThisPackage);
}
List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
PackageManager.GET_META_DATA, mCurrentUserId);
PackageManager.GET_META_DATA, UserHandle.USER_OWNER);
int bestVersion = Integer.MIN_VALUE;
String bestPackage = null;
boolean bestIsMultiuser = false;
for (ResolveInfo rInfo : rInfos) {
String packageName = rInfo.serviceInfo.packageName;
@@ -134,25 +155,30 @@ public class ServiceWatcher implements ServiceConnection {
continue;
}
// check version
int version = 0;
// check metadata
int version = Integer.MIN_VALUE;
boolean isMultiuser = false;
if (rInfo.serviceInfo.metaData != null) {
version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0);
version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION,
Integer.MIN_VALUE);
isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
}
if (version > mVersion) {
bestVersion = version;
bestPackage = packageName;
bestIsMultiuser = isMultiuser;
}
}
if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
(justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
rInfos.size(),
(bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
(bestPackage == null ? "no new best package" : "new best package: "
+ bestPackage)));
if (bestPackage != null) {
bindToPackageLocked(bestPackage, bestVersion);
bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
return true;
}
return false;
@@ -163,21 +189,24 @@ public class ServiceWatcher implements ServiceConnection {
pkg = mPackageName;
mPackageName = null;
mVersion = Integer.MIN_VALUE;
mIsMultiuser = false;
if (pkg != null) {
if (D) Log.d(mTag, "unbinding " + pkg);
mContext.unbindService(this);
}
}
private void bindToPackageLocked(String packageName, int version) {
private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
unbindLocked();
Intent intent = new Intent(mAction);
intent.setPackage(packageName);
mPackageName = packageName;
mVersion = version;
if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
mIsMultiuser = isMultiuser;
if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
+ (isMultiuser ? "multi" : "single") + "-user)");
mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_NOT_VISIBLE, new UserHandle(mCurrentUserId));
| Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.OWNER : UserHandle.CURRENT);
}
public static boolean isSignatureMatch(Signature[] signatures,
@@ -290,11 +319,12 @@ public class ServiceWatcher implements ServiceConnection {
}
}
public void switchUser(int userId) {
public void switchUser() {
synchronized (mLock) {
unbindLocked();
mCurrentUserId = userId;
bindBestPackageLocked(null);
if (!mIsMultiuser) {
unbindLocked();
bindBestPackageLocked(null);
}
}
}
}

View File

@@ -40,8 +40,8 @@ public class GeocoderProxy {
private final ServiceWatcher mServiceWatcher;
public static GeocoderProxy createAndBind(Context context,
List<String> initialPackageNames, Handler handler, int userId) {
GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, handler, userId);
List<String> initialPackageNames, Handler handler) {
GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, handler);
if (proxy.bind()) {
return proxy;
} else {
@@ -49,12 +49,11 @@ public class GeocoderProxy {
}
}
public GeocoderProxy(Context context, List<String> initialPackageNames, Handler handler,
int userId) {
public GeocoderProxy(Context context, List<String> initialPackageNames, Handler handler) {
mContext = context;
mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
null, handler, userId);
null, handler);
}
private boolean bind () {

View File

@@ -793,11 +793,6 @@ 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,8 +38,6 @@ 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

@@ -54,9 +54,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, int userId) {
List<String> initialPackageNames, Handler handler) {
LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
initialPackageNames, handler, userId);
initialPackageNames, handler);
if (proxy.bind()) {
return proxy;
} else {
@@ -65,11 +65,11 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
private LocationProviderProxy(Context context, String name, String action,
List<String> initialPackageNames, Handler handler, int userId) {
List<String> initialPackageNames, Handler handler) {
mContext = context;
mName = name;
mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
mNewServiceWork, handler, userId);
mNewServiceWork, handler);
}
private boolean bind () {
@@ -210,11 +210,6 @@ 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,11 +155,6 @@ 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,11 +96,6 @@ 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 {