ServiceStateProvider:Enforce location permissino check with targetSdkVersion R-
b/182384053 enforce location permission check for apps with targetSdkVersion
S+ to protect the sensitive info. However, for app with targetSdkVersion
R or lower, it does not enforce the same to keep maximum compatibility.
This patch will scrub the location sensitive info in
ServiceStateProvider if the app with targetSdkVersion R- without
location permission.
Bug: 191913126
Test: atest ServiceStateProviderTest ServiceStateTest
Change-Id: Ib30c05952bd1b3e3f7dbf239bbcf6388bf2ab9bd
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
index 08f0907..56786f9 100644
--- a/src/com/android/phone/ServiceStateProvider.java
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -41,6 +41,7 @@
import android.telephony.LocationAccessPolicy;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -397,6 +398,7 @@
return null;
}
+ // TODO(b/182384053): replace targetSdk check with CompatChanges#isChangeEnabled
final boolean targetingAtLeastS = TelephonyPermissions.getTargetSdk(getContext(),
getCallingPackage()) >= Build.VERSION_CODES.S;
final boolean canReadPrivilegedPhoneState = getContext().checkCallingOrSelfPermission(
@@ -412,14 +414,15 @@
} else {
availableColumns = ALL_COLUMNS;
- final boolean hasLocationPermission =
- hasFineLocationPermission() || hasCoarseLocationPermission();
+ final boolean hasLocationPermission = hasLocationPermission();
if (hasLocationPermission) {
+ // No matter the targetSdkVersion, return unredacted ServiceState if caller does
+ // have location permission.
ss = unredactedServiceState;
} else {
- // The caller has no location permission but explicitly requires for location
- // protected columns. Throw SecurityException to fail loudly.
- if (projection != null) {
+ // The caller has targetSdkVersion S+ but no location permission. It explicitly
+ // requires location protected columns. Throw SecurityException to fail loudly.
+ if (targetingAtLeastS && projection != null) {
for (String requiredColumn : projection) {
if (LOCATION_PROTECTED_COLUMNS_SET.contains(requiredColumn)) {
throw new SecurityException("Column " + requiredColumn
@@ -428,15 +431,12 @@
}
}
+ // In all other cases, return the redacted ServiceState.
// The caller has no location permission but only requires columns without
// location sensitive info or "all" columns, return result that scrub out all
// sensitive info. In later case, we will not know which columns will be fetched
// from the returned cursor until the result has been returned.
- ss = unredactedServiceState.createLocationInfoSanitizedCopy(
- true /*removeCoarseLocation*/);
- // TODO(b/188061647): remove the additional redaction once it is fixed in SS
- ss.setCdmaSystemAndNetworkId(ServiceState.UNKNOWN_ID,
- ServiceState.UNKNOWN_ID);
+ ss = getLocationRedactedServiceState(unredactedServiceState);
}
}
@@ -661,8 +661,12 @@
return values;
}
- private boolean hasFineLocationPermission() {
- LocationAccessPolicy.LocationPermissionResult fineLocationResult =
+ /**
+ * Check location permission with same policy as {@link TelephonyManager#getServiceState()}
+ * which enforces location permission check starting from Q.
+ */
+ private boolean hasLocationPermission() {
+ LocationAccessPolicy.LocationPermissionResult locationPermissionResult =
LocationAccessPolicy.checkLocationPermission(getContext(),
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(getCallingPackage())
@@ -671,27 +675,20 @@
.setCallingUid(Binder.getCallingUid())
.setMethod("ServiceStateProvider#query")
.setLogAsInfo(true)
- .setMinSdkVersionForFine(Build.VERSION_CODES.S)
- .setMinSdkVersionForCoarse(Build.VERSION_CODES.S)
- .setMinSdkVersionForEnforcement(Build.VERSION_CODES.S)
+ .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
.build());
- return fineLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ return locationPermissionResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
}
- private boolean hasCoarseLocationPermission() {
- LocationAccessPolicy.LocationPermissionResult coarseLocationResult =
- LocationAccessPolicy.checkLocationPermission(getContext(),
- new LocationAccessPolicy.LocationPermissionQuery.Builder()
- .setCallingPackage(getCallingPackage())
- .setCallingFeatureId(getCallingAttributionTag())
- .setCallingPid(Binder.getCallingPid())
- .setCallingUid(Binder.getCallingUid())
- .setMethod("ServiceStateProvider#query")
- .setLogAsInfo(true)
- .setMinSdkVersionForCoarse(Build.VERSION_CODES.S)
- .setMinSdkVersionForFine(Integer.MAX_VALUE)
- .setMinSdkVersionForEnforcement(Build.VERSION_CODES.S)
- .build());
- return coarseLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ // Return a copy of ServiceState with all sensitive info redacted.
+ @VisibleForTesting
+ /* package */ static ServiceState getLocationRedactedServiceState(ServiceState serviceState) {
+ ServiceState ss =
+ serviceState.createLocationInfoSanitizedCopy(true /*removeCoarseLocation*/);
+ // TODO(b/188061647): remove the additional redaction once it is fixed in SS
+ ss.setCdmaSystemAndNetworkId(ServiceState.UNKNOWN_ID, ServiceState.UNKNOWN_ID);
+ return ss;
}
}