summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictoria Lease <violets@google.com>2012-10-16 16:08:48 -0700
committerVictoria Lease <violets@google.com>2012-10-18 09:13:39 -0700
commit37425c3475877f2fdadb78f669ec57fecf82dca7 (patch)
tree73b33ba2de97ecbb1505d6568f6d329424cbac26
parenta4c356cad7de0ef30faeaeadacd21b6eae913541 (diff)
downloadframeworks_base-37425c3475877f2fdadb78f669ec57fecf82dca7.zip
frameworks_base-37425c3475877f2fdadb78f669ec57fecf82dca7.tar.gz
frameworks_base-37425c3475877f2fdadb78f669ec57fecf82dca7.tar.bz2
LocationManager permissions cleanup
This commit is the result of a comprehensive permissions review for MR1 release. It addresses a number of deviations from spec and from MR0's behavior, bringing MR1 into sync with both. It also cleans up the concept of "location resolution permission", representing it internally as an enumerated access level to reduce reliance on cumbersome string manipulation. There's a function to convert the enum int into a permission string where needed, too. Additionally, this confines caller-identity-sensitive calls to the hopefully-obviously-named "getCallerAllowedResolutionLevel()". This should make it much easier to prove correctness with respect to accidentally calling functions that depend upon the caller's identity after identity has already been shed by Binder.clearCallingIdentity(). Change-Id: I446169aee8fb2fde26ac6d04b479b40253782acb
-rw-r--r--location/java/android/location/LocationRequest.java12
-rw-r--r--services/java/com/android/server/LocationManagerService.java243
2 files changed, 154 insertions, 101 deletions
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index cb291ea..6871ee2 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -221,6 +221,18 @@ public final class LocationRequest implements Parcelable {
/** @hide */
public LocationRequest() { }
+ /** @hide */
+ public LocationRequest(LocationRequest src) {
+ mQuality = src.mQuality;
+ mInterval = src.mInterval;
+ mFastestInterval = src.mFastestInterval;
+ mExplicitFastestInterval = src.mExplicitFastestInterval;
+ mExpireAt = src.mExpireAt;
+ mNumUpdates = src.mNumUpdates;
+ mSmallestDisplacement = src.mSmallestDisplacement;
+ mProvider = src.mProvider;
+ }
+
/**
* Set the quality of the request.
*
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 63eeeb3..468f696 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -90,10 +90,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private static final String WAKELOCK_KEY = TAG;
private static final String THREAD_NAME = TAG;
- private static final String ACCESS_FINE_LOCATION =
- android.Manifest.permission.ACCESS_FINE_LOCATION;
- private static final String ACCESS_COARSE_LOCATION =
- android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ // Location resolution level: no location data whatsoever
+ private static final int RESOLUTION_LEVEL_NONE = 0;
+ // Location resolution level: coarse location data only
+ private static final int RESOLUTION_LEVEL_COARSE = 1;
+ // Location resolution level: fine location data
+ private static final int RESOLUTION_LEVEL_FINE = 2;
+
private static final String ACCESS_MOCK_LOCATION =
android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
@@ -347,7 +350,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
final int mUid; // uid of receiver
final int mPid; // pid of receiver
final String mPackageName; // package name of receiver
- final String mPermission; // best permission that receiver has
+ final int mAllowedResolutionLevel; // resolution level allowed to receiver
final ILocationListener mListener;
final PendingIntent mPendingIntent;
@@ -366,7 +369,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
mKey = intent;
}
- mPermission = checkPermission();
+ mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
mUid = uid;
mPid = pid;
mPackageName = packageName;
@@ -440,7 +443,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
- mPermission);
+ getResolutionPermission(mAllowedResolutionLevel));
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -474,7 +477,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
- mPermission);
+ getResolutionPermission(mAllowedResolutionLevel));
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -512,7 +515,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
- mPermission);
+ getResolutionPermission(mAllowedResolutionLevel));
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -609,51 +612,76 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
/**
- * Returns the best permission available to the caller.
+ * Returns the permission string associated with the specified resolution level.
+ *
+ * @param resolutionLevel the resolution level
+ * @return the permission string
*/
- private String getBestCallingPermission() {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
- PackageManager.PERMISSION_GRANTED) {
- return ACCESS_FINE_LOCATION;
- } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
- PackageManager.PERMISSION_GRANTED) {
- return ACCESS_COARSE_LOCATION;
+ private String getResolutionPermission(int resolutionLevel) {
+ switch (resolutionLevel) {
+ case RESOLUTION_LEVEL_FINE:
+ return android.Manifest.permission.ACCESS_FINE_LOCATION;
+ case RESOLUTION_LEVEL_COARSE:
+ return android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ default:
+ return null;
}
- return null;
}
/**
- * Throw SecurityException if caller has neither COARSE or FINE.
- * Otherwise, return the best permission.
+ * Returns the resolution level allowed to the given PID/UID pair.
+ *
+ * @param pid the PID
+ * @param uid the UID
+ * @return resolution level allowed to the pid/uid pair
*/
- private String checkPermission() {
- String perm = getBestCallingPermission();
- if (perm == null) {
- throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
- " ACCESS_FINE_LOCATION permission");
+ private int getAllowedResolutionLevel(int pid, int uid) {
+ if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_FINE;
+ } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_COARSE;
+ } else {
+ return RESOLUTION_LEVEL_NONE;
}
- return perm;
}
/**
- * Throw SecurityException if caller lacks permission to use Geofences.
+ * Returns the resolution level allowed to the caller
+ *
+ * @return resolution level allowed to caller
+ */
+ private int getCallerAllowedResolutionLevel() {
+ return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
+ }
+
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use geofences.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
*/
- private void checkGeofencePermission() {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
- PackageManager.PERMISSION_GRANTED) {
+ private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
}
}
- private String getMinimumPermissionForProvider(String provider) {
+ /**
+ * Return the minimum resolution level required to use the specified location provider.
+ *
+ * @param provider the name of the location provider
+ * @return minimum resolution level required for provider
+ */
+ private int getMinimumResolutionLevelForProviderUse(String provider) {
if (LocationManager.GPS_PROVIDER.equals(provider) ||
LocationManager.PASSIVE_PROVIDER.equals(provider)) {
// gps and passive providers require FINE permission
- return ACCESS_FINE_LOCATION;
+ return RESOLUTION_LEVEL_FINE;
} else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
LocationManager.FUSED_PROVIDER.equals(provider)) {
// network and fused providers are ok with COARSE or FINE
- return ACCESS_COARSE_LOCATION;
+ return RESOLUTION_LEVEL_COARSE;
} else {
// mock providers
LocationProviderInterface lp = mMockProviders.get(provider);
@@ -662,41 +690,38 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (properties != null) {
if (properties.mRequiresSatellite) {
// provider requiring satellites require FINE permission
- return ACCESS_FINE_LOCATION;
+ return RESOLUTION_LEVEL_FINE;
} else if (properties.mRequiresNetwork || properties.mRequiresCell) {
// provider requiring network and or cell require COARSE or FINE
- return ACCESS_COARSE_LOCATION;
+ return RESOLUTION_LEVEL_COARSE;
}
}
}
}
-
- return null;
- }
-
- private boolean isPermissionSufficient(String perm, String minPerm) {
- if (ACCESS_FINE_LOCATION.equals(minPerm)) {
- return ACCESS_FINE_LOCATION.equals(perm);
- } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) {
- return ACCESS_FINE_LOCATION.equals(perm) ||
- ACCESS_COARSE_LOCATION.equals(perm);
- } else {
- return false;
- }
+ return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
}
- private void checkPermissionForProvider(String perm, String provider) {
- String minPerm = getMinimumPermissionForProvider(provider);
- if (!isPermissionSufficient(perm, minPerm)) {
- if (ACCESS_FINE_LOCATION.equals(minPerm)) {
- throw new SecurityException("Location provider \"" + provider +
- "\" requires ACCESS_FINE_LOCATION permission.");
- } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) {
- throw new SecurityException("Location provider \"" + provider +
- "\" requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");
- } else {
- throw new SecurityException("Insufficient permission for location provider \"" +
- provider + "\".");
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use the named
+ * location provider.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
+ * @param providerName the name of the location provider
+ */
+ private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
+ String providerName) {
+ int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
+ if (allowedResolutionLevel < requiredResolutionLevel) {
+ switch (requiredResolutionLevel) {
+ case RESOLUTION_LEVEL_FINE:
+ throw new SecurityException("\"" + providerName + "\" location provider " +
+ "requires ACCESS_FINE_LOCATION permission.");
+ case RESOLUTION_LEVEL_COARSE:
+ throw new SecurityException("\"" + providerName + "\" location provider " +
+ "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");
+ default:
+ throw new SecurityException("Insufficient permission for \"" + providerName +
+ "\" location provider.");
}
}
}
@@ -731,8 +756,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
@Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
ArrayList<String> out;
- String perm = getBestCallingPermission();
int callingUserId = UserHandle.getCallingUserId();
long identity = Binder.clearCallingIdentity();
try {
@@ -743,7 +768,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
}
- if (isPermissionSufficient(perm, getMinimumPermissionForProvider(name))) {
+ if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
continue;
}
@@ -803,8 +828,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- checkPermission();
-
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
@@ -1010,33 +1033,41 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return receiver;
}
- private String checkPermissionAndRequest(LocationRequest request) {
- String perm = getBestCallingPermission();
- String provider = request.getProvider();
- checkPermissionForProvider(perm, provider);
-
- if (ACCESS_COARSE_LOCATION.equals(perm)) {
- switch (request.getQuality()) {
+ /**
+ * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution
+ * and consistency requirements.
+ *
+ * @param request the LocationRequest from which to create a sanitized version
+ * @param shouldBeCoarse whether the sanitized version should be held to coarse resolution
+ * constraints
+ * @param fastestCoarseIntervalMS minimum interval allowed for coarse resolution
+ * @return a version of request that meets the given resolution and consistency requirements
+ * @hide
+ */
+ private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel) {
+ LocationRequest sanitizedRequest = new LocationRequest(request);
+ if (resolutionLevel < RESOLUTION_LEVEL_FINE) {
+ switch (sanitizedRequest.getQuality()) {
case LocationRequest.ACCURACY_FINE:
- request.setQuality(LocationRequest.ACCURACY_BLOCK);
+ sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK);
break;
case LocationRequest.POWER_HIGH:
- request.setQuality(LocationRequest.POWER_LOW);
+ sanitizedRequest.setQuality(LocationRequest.POWER_LOW);
break;
}
// throttle
- if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
- request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
+ if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+ sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
}
- if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
- request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
+ if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+ sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
}
}
// make getFastestInterval() the minimum of interval and fastest interval
- if (request.getFastestInterval() > request.getInterval()) {
+ if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) {
request.setFastestInterval(request.getInterval());
}
- return perm;
+ return sanitizedRequest;
}
private void checkPackageName(String packageName) {
@@ -1079,7 +1110,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
PendingIntent intent, String packageName) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
- checkPermissionAndRequest(request);
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
+ request.getProvider());
+ LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -1089,7 +1123,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1132,7 +1166,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
public void removeUpdates(ILocationListener listener, PendingIntent intent,
String packageName) {
checkPackageName(packageName);
- checkPermission();
+
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
@@ -1188,8 +1222,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
public Location getLastLocation(LocationRequest request, String packageName) {
if (D) Log.d(TAG, "getLastLocation: " + request);
if (request == null) request = DEFAULT_LOCATION_REQUEST;
- String perm = checkPermissionAndRequest(request);
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkPackageName(packageName);
+ checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
+ request.getProvider());
+ // no need to sanitize this request, as only the provider name is used
long identity = Binder.clearCallingIdentity();
try {
@@ -1213,13 +1250,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (location == null) {
return null;
}
- if (ACCESS_FINE_LOCATION.equals(perm)) {
- return location;
- } else {
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation != null) {
return mLocationFudger.getOrCreate(noGPSLocation);
}
+ } else {
+ return location;
}
}
return null;
@@ -1232,18 +1269,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
String packageName) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
- checkGeofencePermission();
- checkPermissionAndRequest(request);
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
checkPendingIntent(intent);
checkPackageName(packageName);
+ checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
+ request.getProvider());
+ LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
- if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
+ if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
// geo-fence manager uses the public location API, need to clear identity
int uid = Binder.getCallingUid();
long identity = Binder.clearCallingIdentity();
try {
- mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
+ mGeofenceManager.addFence(sanitizedRequest, geofence, intent, uid, packageName);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1251,7 +1291,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- checkGeofencePermission();
+ checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel());
checkPendingIntent(intent);
checkPackageName(packageName);
@@ -1272,10 +1312,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (mGpsStatusProvider == null) {
return false;
}
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
- PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
- }
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ LocationManager.GPS_PROVIDER);
try {
mGpsStatusProvider.addGpsStatusListener(listener);
@@ -1303,8 +1341,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// throw NullPointerException to remain compatible with previous implementation
throw new NullPointerException();
}
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ provider);
- checkPermission();
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1344,7 +1383,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return null;
}
- checkPermissionForProvider(getBestCallingPermission(), provider);
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ provider);
LocationProviderInterface p;
synchronized (mLock) {
@@ -1357,7 +1397,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public boolean isProviderEnabled(String provider) {
- checkPermissionForProvider(getBestCallingPermission(), provider);
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ provider);
if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
long identity = Binder.clearCallingIdentity();
@@ -1522,10 +1563,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
Location notifyLocation = null;
- if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
- notifyLocation = lastLocation; // use fine location
+ if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ notifyLocation = coarseLocation; // use coarse location
} else {
- notifyLocation = coarseLocation; // use coarse location if available
+ notifyLocation = lastLocation; // use fine location
}
if (notifyLocation != null) {
Location lastLoc = r.mLastFixBroadcast;