Merge "Fix bugs in LocationManagerService and AppOpsPolicy" into sc-dev

This commit is contained in:
Soonil Nagarkar
2021-06-16 15:37:32 +00:00
committed by Android (Google) Code Review
9 changed files with 350 additions and 238 deletions

View File

@@ -23,20 +23,26 @@ import android.annotation.TestApi;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.Immutable;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* A list of packages and associated attribution tags that supports easy membership checks.
* A list of packages and associated attribution tags that supports easy membership checks. Supports
* "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to
* standard checks.
*
* @hide
*/
@TestApi
@Immutable
public final class PackageTagsList implements Parcelable {
// an empty set value matches any attribution tag
// an empty set value matches any attribution tag (ie, wildcard)
private final ArrayMap<String, ArraySet<String>> mPackageTags;
private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) {
@@ -51,14 +57,33 @@ public final class PackageTagsList implements Parcelable {
}
/**
* Returns true if the given package is represented within this instance. If this returns true
* this does not imply anything about whether any given attribution tag under the given package
* name is present.
* Returns true if the given package is found within this instance. If this returns true this
* does not imply anything about whether any given attribution tag under the given package name
* is present.
*/
public boolean includes(@NonNull String packageName) {
return mPackageTags.containsKey(packageName);
}
/**
* Returns true if the given attribution tag is found within this instance under any package.
* Only returns true if the attribution tag literal is found, not if any package contains the
* set of all attribution tags.
*
* @hide
*/
public boolean includesTag(@NonNull String attributionTag) {
final int size = mPackageTags.size();
for (int i = 0; i < size; i++) {
ArraySet<String> tags = mPackageTags.valueAt(i);
if (tags.contains(attributionTag)) {
return true;
}
}
return false;
}
/**
* Returns true if all attribution tags under the given package are contained within this
* instance.
@@ -76,6 +101,7 @@ public final class PackageTagsList implements Parcelable {
if (tags == null) {
return false;
} else if (tags.isEmpty()) {
// our tags are the full set, so we contain any attribution tag
return true;
} else {
return tags.contains(attributionTag);
@@ -98,10 +124,12 @@ public final class PackageTagsList implements Parcelable {
return false;
}
if (tags.isEmpty()) {
// our tags are the full set, so we contain whatever the other tags are
continue;
}
ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i);
if (otherTags.isEmpty()) {
// other tags are the full set, so we can't contain them
return false;
}
if (!tags.containsAll(otherTags)) {
@@ -247,6 +275,31 @@ public final class PackageTagsList implements Parcelable {
return this;
}
/**
* Adds the specified package and set of attribution tags to the builder.
*
* @hide
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder add(@NonNull String packageName,
@NonNull Collection<String> attributionTags) {
if (attributionTags.isEmpty()) {
// the input is not allowed to specify a full set by passing in an empty collection
return this;
}
ArraySet<String> tags = mPackageTags.get(packageName);
if (tags == null) {
tags = new ArraySet<>(attributionTags);
mPackageTags.put(packageName, tags);
} else if (!tags.isEmpty()) {
// if we contain the full set, already done, otherwise add all the tags
tags.addAll(attributionTags);
}
return this;
}
/**
* Adds the specified {@link PackageTagsList} to the builder.
*/
@@ -267,13 +320,92 @@ public final class PackageTagsList implements Parcelable {
if (newTags.isEmpty()) {
add(entry.getKey());
} else {
ArraySet<String> tags = mPackageTags.get(entry.getKey());
if (tags == null) {
tags = new ArraySet<>(newTags);
mPackageTags.put(entry.getKey(), tags);
} else if (!tags.isEmpty()) {
tags.addAll(newTags);
}
add(entry.getKey(), newTags);
}
}
return this;
}
/**
* Removes all attribution tags under the specified package from the builder.
*
* @hide
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder remove(@NonNull String packageName) {
mPackageTags.remove(packageName);
return this;
}
/**
* Removes the specified package and attribution tag from the builder if and only if the
* specified attribution tag is listed explicitly under the package. If the package contains
* all possible attribution tags, then nothing will be removed.
*
* @hide
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder remove(@NonNull String packageName,
@Nullable String attributionTag) {
ArraySet<String> tags = mPackageTags.get(packageName);
if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) {
mPackageTags.remove(packageName);
}
return this;
}
/**
* Removes the specified package and set of attribution tags from the builder if and only if
* the specified set of attribution tags are listed explicitly under the package. If the
* package contains all possible attribution tags, then nothing will be removed.
*
* @hide
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder remove(@NonNull String packageName,
@NonNull Collection<String> attributionTags) {
if (attributionTags.isEmpty()) {
// the input is not allowed to specify a full set by passing in an empty collection
return this;
}
ArraySet<String> tags = mPackageTags.get(packageName);
if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) {
mPackageTags.remove(packageName);
}
return this;
}
/**
* Removes the specified {@link PackageTagsList} from the builder. If a package contains all
* possible attribution tags, it will only be removed if the package in the removed
* {@link PackageTagsList} also contains all possible attribution tags.
*
* @hide
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder remove(@NonNull PackageTagsList packageTagsList) {
return remove(packageTagsList.mPackageTags);
}
/**
* Removes the given map of package to attribution tags to the builder. An empty set of
* attribution tags is interpreted to imply all attribution tags under that package. If a
* package contains all possible attribution tags, it will only be removed if the package in
* the removed map also contains all possible attribution tags.
*
* @hide
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder remove(@NonNull Map<String, ? extends Set<String>> packageTagsMap) {
for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
Set<String> removedTags = entry.getValue();
if (removedTags.isEmpty()) {
// if removing the full set, drop the package completely
remove(entry.getKey());
} else {
remove(entry.getKey(), removedTags);
}
}

View File

@@ -30,6 +30,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Collections;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -40,7 +41,8 @@ public class PackageTagsListTest {
PackageTagsList.Builder builder = new PackageTagsList.Builder()
.add("package1", "attr1")
.add("package1", "attr2")
.add("package2");
.add("package2")
.add("package4", Arrays.asList("attr1", "attr2"));
PackageTagsList list = builder.build();
assertTrue(list.contains(builder.build()));
@@ -49,10 +51,13 @@ public class PackageTagsListTest {
assertTrue(list.contains("package2", "attr1"));
assertTrue(list.contains("package2", "attr2"));
assertTrue(list.contains("package2", "attr3"));
assertTrue(list.contains("package4", "attr1"));
assertTrue(list.contains("package4", "attr2"));
assertTrue(list.containsAll("package2"));
assertTrue(list.includes("package1"));
assertTrue(list.includes("package2"));
assertFalse(list.contains("package1", "attr3"));
assertFalse(list.contains("package4", "attr3"));
assertFalse(list.containsAll("package1"));
assertFalse(list.includes("package3"));
@@ -91,6 +96,51 @@ public class PackageTagsListTest {
assertFalse(list.contains(bigList));
}
@Test
public void testPackageTagsList_Remove() {
PackageTagsList.Builder builder = new PackageTagsList.Builder()
.add("package1", "attr1")
.add("package1", "attr2")
.add("package2")
.add("package4", Arrays.asList("attr1", "attr2", "attr3"))
.add("package3", "attr1")
.remove("package1", "attr1")
.remove("package1", "attr2")
.remove("package2", "attr1")
.remove("package4", Arrays.asList("attr1", "attr2"))
.remove("package3");
PackageTagsList list = builder.build();
assertTrue(list.contains(builder.build()));
assertFalse(list.contains("package1", "attr1"));
assertFalse(list.contains("package1", "attr2"));
assertTrue(list.contains("package2", "attr1"));
assertTrue(list.contains("package2", "attr2"));
assertTrue(list.contains("package2", "attr3"));
assertFalse(list.contains("package3", "attr1"));
assertFalse(list.contains("package4", "attr1"));
assertFalse(list.contains("package4", "attr2"));
assertTrue(list.contains("package4", "attr3"));
assertTrue(list.containsAll("package2"));
assertFalse(list.includes("package1"));
assertTrue(list.includes("package2"));
assertFalse(list.includes("package3"));
assertTrue(list.includes("package4"));
}
@Test
public void testPackageTagsList_EmptyCollections() {
PackageTagsList.Builder builder = new PackageTagsList.Builder()
.add("package1", Collections.emptyList())
.add("package2")
.remove("package2", Collections.emptyList());
PackageTagsList list = builder.build();
assertTrue(list.contains(builder.build()));
assertFalse(list.contains("package1", "attr1"));
assertTrue(list.contains("package2", "attr2"));
}
@Test
public void testWriteToParcel() {
PackageTagsList list = new PackageTagsList.Builder()

View File

@@ -16,14 +16,10 @@
package android.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
import com.android.internal.annotations.Immutable;
import java.util.Set;
import android.os.PackageTagsList;
/**
* Location manager local system service interface.
@@ -43,18 +39,14 @@ public abstract class LocationManagerInternal {
}
/**
* Interface for getting callbacks when a location provider's location tags change.
*
* @see LocationTagInfo
* Interface for getting callbacks when an app id's location provider package tags change.
*/
public interface OnProviderLocationTagsChangeListener {
public interface LocationPackageTagsListener {
/**
* Called when the location tags for a provider change.
*
* @param providerLocationTagInfo The tag info for a provider.
* Called when the package tags for a location provider change for a uid.
*/
void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo);
void onLocationPackageTagsChanged(int uid, @NonNull PackageTagsList packageTagsList);
}
/**
@@ -109,58 +101,9 @@ public abstract class LocationManagerInternal {
public abstract @Nullable LocationTime getGnssTimeMillis();
/**
* Sets a listener for changes in the location providers' tags. Passing
* Sets a listener for changes in an app id's location provider package tags. Passing
* {@code null} clears the current listener.
*
* @param listener The listener.
*/
public abstract void setOnProviderLocationTagsChangeListener(
@Nullable OnProviderLocationTagsChangeListener listener);
/**
* This class represents the location permission tags used by the location provider
* packages in a given UID. These tags are strictly used for accessing state guarded
* by the location permission(s) by a location provider which are required for the
* provider to fulfill its function as being a location provider.
*/
@Immutable
public static class LocationTagInfo {
private final int mUid;
@NonNull
private final String mPackageName;
@Nullable
private final Set<String> mLocationTags;
public LocationTagInfo(int uid, @NonNull String packageName,
@Nullable Set<String> locationTags) {
mUid = uid;
mPackageName = packageName;
mLocationTags = locationTags;
}
/**
* @return The UID for which tags are related.
*/
public int getUid() {
return mUid;
}
/**
* @return The package for which tags are related.
*/
@NonNull
public String getPackageName() {
return mPackageName;
}
/**
* @return The tags for the package used for location related accesses.
*/
@Nullable
public Set<String> getTags() {
return mLocationTags;
}
}
public abstract void setLocationPackageTagsListener(
@Nullable LocationPackageTagsListener listener);
}

View File

@@ -65,7 +65,7 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
@@ -93,6 +93,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.eventlog.LocationEventLog;
@@ -259,7 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
@Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
@Nullable LocationPackageTagsListener mLocationTagsChangedListener;
LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
@@ -1363,32 +1364,28 @@ public class LocationManagerService extends ILocationManager.Stub implements
refreshAppOpsRestrictions(UserHandle.USER_ALL);
}
OnProviderLocationTagsChangeListener listener;
synchronized (mLock) {
listener = mOnProviderLocationTagsChangeListener;
}
if (listener != null) {
if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
|| !Objects.equals(oldState.identity, newState.identity)) {
if (oldState.identity != null) {
listener.onLocationTagsChanged(
new LocationManagerInternal.LocationTagInfo(
oldState.identity.getUid(),
oldState.identity.getPackageName(),
Collections.emptySet()));
}
if (newState.identity != null) {
ArraySet<String> attributionTags = new ArraySet<>(
newState.extraAttributionTags.size() + 1);
attributionTags.addAll(newState.extraAttributionTags);
attributionTags.add(newState.identity.getAttributionTag());
listener.onLocationTagsChanged(
new LocationManagerInternal.LocationTagInfo(
newState.identity.getUid(),
newState.identity.getPackageName(),
attributionTags));
if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
|| !Objects.equals(oldState.identity, newState.identity)) {
// since we're potentially affecting the tag lists for two different uids, acquire the
// lock to ensure providers cannot change while we're looping over the providers
// multiple times, which could lead to inconsistent results.
synchronized (mLock) {
LocationPackageTagsListener listener = mLocationTagsChangedListener;
if (listener != null) {
int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1;
int newUid = newState.identity != null ? newState.identity.getUid() : -1;
if (oldUid != -1) {
PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid);
FgThread.getHandler().post(
() -> listener.onLocationPackageTagsChanged(oldUid, tags));
}
// if the new app id is the same as the old app id, no need to invoke the
// listener twice, it's already been taken care of
if (newUid != -1 && newUid != oldUid) {
PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid);
FgThread.getHandler().post(
() -> listener.onLocationPackageTagsChanged(newUid, tags));
}
}
}
}
@@ -1436,6 +1433,31 @@ public class LocationManagerService extends ILocationManager.Stub implements
userId);
}
PackageTagsList calculateAppOpsLocationSourceTags(int uid) {
PackageTagsList.Builder builder = new PackageTagsList.Builder();
for (LocationProviderManager manager : mProviderManagers) {
AbstractLocationProvider.State managerState = manager.getState();
if (managerState.identity == null) {
continue;
}
if (managerState.identity.getUid() != uid) {
continue;
}
builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags);
if (managerState.extraAttributionTags.isEmpty()
|| managerState.identity.getAttributionTag() != null) {
builder.add(managerState.identity.getPackageName(),
managerState.identity.getAttributionTag());
} else {
Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and "
+ "a non-empty set of extra attribution tags - dropping the null "
+ "attribution tag");
}
}
return builder.build();
}
private class LocalService extends LocationManagerInternal {
LocalService() {}
@@ -1506,10 +1528,29 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
public void setOnProviderLocationTagsChangeListener(
@Nullable OnProviderLocationTagsChangeListener listener) {
public void setLocationPackageTagsListener(
@Nullable LocationPackageTagsListener listener) {
synchronized (mLock) {
mOnProviderLocationTagsChangeListener = listener;
mLocationTagsChangedListener = listener;
// calculate initial tag list and send to listener
if (listener != null) {
ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size());
for (LocationProviderManager manager : mProviderManagers) {
CallerIdentity identity = manager.getIdentity();
if (identity != null) {
uids.add(identity.getUid());
}
}
for (int uid : uids) {
PackageTagsList tags = calculateAppOpsLocationSourceTags(uid);
if (!tags.isEmpty()) {
FgThread.getHandler().post(
() -> listener.onLocationPackageTagsChanged(uid, tags));
}
}
}
}
}
}

View File

@@ -263,10 +263,10 @@ public abstract class AbstractLocationProvider {
}
/**
* The current allowed state of this provider.
* The current state of the provider.
*/
public final boolean isAllowed() {
return mInternalState.get().state.allowed;
public final State getState() {
return mInternalState.get().state;
}
/**
@@ -276,13 +276,6 @@ public abstract class AbstractLocationProvider {
setState(state -> state.withAllowed(allowed));
}
/**
* The current provider properties of this provider.
*/
public final @Nullable ProviderProperties getProperties() {
return mInternalState.get().state.properties;
}
/**
* Call this method to report a change in provider properties.
*/
@@ -290,13 +283,6 @@ public abstract class AbstractLocationProvider {
setState(state -> state.withProperties(properties));
}
/**
* The current identity of this provider.
*/
public final @Nullable CallerIdentity getIdentity() {
return mInternalState.get().state.identity;
}
/**
* Call this method to report a change in the provider's identity.
*/

View File

@@ -1407,12 +1407,16 @@ public class LocationProviderManager extends
return mName;
}
public AbstractLocationProvider.State getState() {
return mProvider.getState();
}
public @Nullable CallerIdentity getIdentity() {
return mProvider.getIdentity();
return mProvider.getState().identity;
}
public @Nullable ProviderProperties getProperties() {
return mProvider.getProperties();
return mProvider.getState().properties;
}
public boolean hasProvider() {
@@ -2403,7 +2407,7 @@ public class LocationProviderManager extends
Preconditions.checkArgument(userId >= 0);
boolean enabled = mState == STATE_STARTED
&& mProvider.isAllowed()
&& mProvider.getState().allowed
&& mSettingsHelper.isLocationEnabled(userId);
int index = mEnabled.indexOfKey(userId);

View File

@@ -21,9 +21,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
@@ -32,7 +30,6 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Set;
/**
* Represents a location provider that may switch between a mock implementation and a real
@@ -290,21 +287,21 @@ public class MockableLocationProvider extends AbstractLocationProvider {
Preconditions.checkState(!Thread.holdsLock(mOwnerLock));
AbstractLocationProvider provider;
State providerState;
synchronized (mOwnerLock) {
provider = mProvider;
pw.println("allowed=" + isAllowed());
CallerIdentity identity = getIdentity();
if (identity != null) {
pw.println("identity=" + identity);
}
Set<String> extraAttributionTags = getExtraAttributionTags();
if (!extraAttributionTags.isEmpty()) {
pw.println("extra attribution tags=" + extraAttributionTags);
}
ProviderProperties properties = getProperties();
if (properties != null) {
pw.println("properties=" + properties);
}
providerState = getState();
}
pw.println("allowed=" + providerState.allowed);
if (providerState.identity != null) {
pw.println("identity=" + providerState.identity);
}
if (!providerState.extraAttributionTags.isEmpty()) {
pw.println("extra attribution tags=" + providerState.extraAttributionTags);
}
if (providerState.properties != null) {
pw.println("properties=" + providerState.properties);
}
if (provider != null) {

View File

@@ -33,14 +33,15 @@ import android.content.pm.ResolveInfo;
import android.location.LocationManagerInternal;
import android.net.Uri;
import android.os.IBinder;
import android.os.PackageTagsList;
import android.os.Process;
import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.DecFunction;
@@ -52,9 +53,9 @@ import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -67,11 +68,10 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
"android:activity_recognition_allow_listed_tags";
private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
private static ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
private static final ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
"awareness_provider", "activity_recognition_provider", "network_location_provider",
"network_location_calibration", "fused_location_provider", "geofencer_provider"});
@NonNull
private final Object mLock = new Object();
@@ -96,13 +96,22 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
*/
@GuardedBy("mLock - writes only - see above")
@NonNull
private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
private final ConcurrentHashMap<Integer, PackageTagsList> mLocationTags =
new ConcurrentHashMap<>();
// location tags can vary per uid - but we merge all tags under an app id into the final data
// structure above
@GuardedBy("mLock")
private final SparseArray<PackageTagsList> mPerUidLocationTags = new SparseArray<>();
// activity recognition currently only grabs tags from the APK manifest. we know that the
// manifest is the same for all users, so there's no need to track variations in tags across
// different users. if that logic ever changes, this might need to behave more like location
// tags above.
@GuardedBy("mLock - writes only - see above")
@NonNull
private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
mActivityRecognitionTags = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, PackageTagsList> mActivityRecognitionTags =
new ConcurrentHashMap<>();
public AppOpsPolicy(@NonNull Context context) {
mContext = context;
@@ -112,13 +121,28 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class);
locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
synchronized (mLock) {
updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
providerTagInfo.getPackageName(), providerTagInfo.getTags(),
mLocationTags);
}
});
locationManagerInternal.setLocationPackageTagsListener(
(uid, packageTagsList) -> {
synchronized (mLock) {
if (packageTagsList.isEmpty()) {
mPerUidLocationTags.remove(uid);
} else {
mPerUidLocationTags.set(uid, packageTagsList);
}
int appId = UserHandle.getAppId(uid);
PackageTagsList.Builder appIdTags = new PackageTagsList.Builder(1);
int size = mPerUidLocationTags.size();
for (int i = 0; i < size; i++) {
if (UserHandle.getAppId(mPerUidLocationTags.keyAt(i)) == appId) {
appIdTags.add(mPerUidLocationTags.valueAt(i));
}
}
updateAllowListedTagsForPackageLocked(appId, appIdTags.build(),
mLocationTags);
}
});
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -306,95 +330,30 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
}
final String tagsList = resolvedService.serviceInfo.metaData.getString(
ACTIVITY_RECOGNITION_TAGS);
if (tagsList != null) {
final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
if (!TextUtils.isEmpty(tagsList)) {
PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add(
resolvedService.serviceInfo.packageName,
Arrays.asList(tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR))).build();
synchronized (mLock) {
updateAllowListedTagsForPackageLocked(
resolvedService.serviceInfo.applicationInfo.uid,
resolvedService.serviceInfo.packageName, new ArraySet<>(tags),
UserHandle.getAppId(resolvedService.serviceInfo.applicationInfo.uid),
packageTagsList,
mActivityRecognitionTags);
}
}
}
private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
ArraySet<String>>> datastore) {
final int appId = UserHandle.getAppId(uid);
// We make a copy of the per UID state to limit our mutation to one
// operation in the underlying concurrent data structure.
ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
if (appIdTags != null) {
appIdTags = new ArrayMap<>(appIdTags);
}
ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
if (packageTags != null) {
packageTags = new ArraySet<>(packageTags);
}
if (allowListedTags != null && !allowListedTags.isEmpty()) {
if (packageTags != null) {
packageTags.clear();
packageTags.addAll(allowListedTags);
} else {
packageTags = new ArraySet<>(allowListedTags);
}
if (appIdTags == null) {
appIdTags = new ArrayMap<>();
}
// Remove any invalid tags
boolean nullRemoved = packageTags.remove(null);
boolean nullStrRemoved = packageTags.remove("null");
boolean emptyRemoved = packageTags.remove("");
if (nullRemoved || nullStrRemoved || emptyRemoved) {
Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed "
+ "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved
+ " removed empty string: " + emptyRemoved);
}
appIdTags.put(packageName, packageTags);
datastore.put(appId, appIdTags);
} else if (appIdTags != null) {
appIdTags.remove(packageName);
if (!appIdTags.isEmpty()) {
datastore.put(appId, appIdTags);
} else {
datastore.remove(appId);
}
}
private static void updateAllowListedTagsForPackageLocked(int appId,
PackageTagsList packageTagsList,
ConcurrentHashMap<Integer, PackageTagsList> datastore) {
datastore.put(appId, packageTagsList);
}
private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
@NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
ArraySet<String>>> mappedOps) {
@NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps) {
// Only a single lookup from the underlying concurrent data structure
final int appId = UserHandle.getAppId(uid);
final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
if (appIdTags != null) {
final ArraySet<String> packageTags = appIdTags.get(packageName);
if (packageTags != null && packageTags.contains(attributionTag)) {
if (packageName.equals("com.google.android.gms")
&& !sExpectedTags.contains(attributionTag)) {
Log.i("AppOpsDebugRemapping", packageName + " tag "
+ attributionTag + " in " + packageTags);
}
return true;
}
if (packageName.equals("com.google.android.gms")
&& sExpectedTags.contains(attributionTag)) {
Log.i("AppOpsDebugRemapping", packageName + " tag " + attributionTag
+ " NOT in " + packageTags);
}
} else {
if (packageName.equals("com.google.android.gms")) {
Log.i("AppOpsDebugRemapping", "no package tags for uid " + uid
+ " package " + packageName);
}
}
return false;
final PackageTagsList appIdTags = mappedOps.get(UserHandle.getAppId(uid));
return appIdTags != null && appIdTags.contains(packageName, attributionTag);
}
private static int resolveLocationOp(int code) {

View File

@@ -158,7 +158,7 @@ public class MockableLocationProviderTest {
@Test
public void testSetState() {
assertThat(mProvider.isAllowed()).isFalse();
assertThat(mProvider.getState().allowed).isFalse();
AbstractLocationProvider.State newState;