diff options
12 files changed, 132 insertions, 72 deletions
diff --git a/packages/FusedLocation/Android.mk b/packages/FusedLocation/Android.mk index a81b9f16cf38..318782f4c1d2 100644 --- a/packages/FusedLocation/Android.mk +++ b/packages/FusedLocation/Android.mk @@ -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) diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml index 779428e34a0c..6a4d4bfc856a 100644 --- a/packages/FusedLocation/AndroidManifest.xml +++ b/packages/FusedLocation/AndroidManifest.xml @@ -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> diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java index 79188823014b..56feb4726dd6 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java @@ -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); } /** diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java index f9091586b7fb..4ba6c342b85e 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java @@ -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; + } } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 9606cf1b83a7..a4a22efadcd8 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -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 (mGeocodeProvider != null) { - if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return; - } - for (LocationProviderProxy proxy : mProxyProviders) { - if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return; + if (isUidALocationProvider(Binder.getCallingUid())) { + 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); } } diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java index bca7faae484d..6078d8ad4530 100644 --- a/services/java/com/android/server/ServiceWatcher.java +++ b/services/java/com/android/server/ServiceWatcher.java @@ -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); + } } } } diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java index cdbfcbc492b5..810369572d8d 100644 --- a/services/java/com/android/server/location/GeocoderProxy.java +++ b/services/java/com/android/server/location/GeocoderProxy.java @@ -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 () { diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index da150d9e6ca6..c1e4a3204af1 100644 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -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); diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java index 80e71f179ed4..6f0923233e19 100644 --- a/services/java/com/android/server/location/LocationProviderInterface.java +++ b/services/java/com/android/server/location/LocationProviderInterface.java @@ -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) --- diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java index cec77a8cca01..7faf72c33284 100644 --- a/services/java/com/android/server/location/LocationProviderProxy.java +++ b/services/java/com/android/server/location/LocationProviderProxy.java @@ -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 () { @@ -211,11 +211,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"); 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 1194cbcd2d0d..36c43ff1ce1d 100644 --- a/services/java/com/android/server/location/MockProvider.java +++ b/services/java/com/android/server/location/MockProvider.java @@ -156,11 +156,6 @@ public class MockProvider implements LocationProviderInterface { 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 734c572397a4..71bae07680a3 100644 --- a/services/java/com/android/server/location/PassiveProvider.java +++ b/services/java/com/android/server/location/PassiveProvider.java @@ -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 { |