Loading core/java/android/os/PackageTagsList.java +144 −12 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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. Loading @@ -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); Loading @@ -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)) { Loading Loading @@ -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. */ Loading @@ -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); } } Loading core/tests/coretests/src/android/os/PackageTagsListTest.java +51 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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())); Loading @@ -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")); Loading Loading @@ -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() Loading location/java/android/location/LocationManagerInternal.java +8 −65 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); } /** Loading Loading @@ -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); } services/core/java/com/android/server/location/LocationManagerService.java +72 −31 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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())); // 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)); } 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)); } } } Loading Loading @@ -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() {} Loading Loading @@ -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)); } } } } } } Loading services/core/java/com/android/server/location/provider/AbstractLocationProvider.java +3 −17 Original line number Diff line number Diff line Loading @@ -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; } /** Loading @@ -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. */ Loading @@ -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. */ Loading Loading
core/java/android/os/PackageTagsList.java +144 −12 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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. Loading @@ -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); Loading @@ -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)) { Loading Loading @@ -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. */ Loading @@ -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); } } Loading
core/tests/coretests/src/android/os/PackageTagsListTest.java +51 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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())); Loading @@ -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")); Loading Loading @@ -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() Loading
location/java/android/location/LocationManagerInternal.java +8 −65 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); } /** Loading Loading @@ -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); }
services/core/java/com/android/server/location/LocationManagerService.java +72 −31 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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())); // 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)); } 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)); } } } Loading Loading @@ -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() {} Loading Loading @@ -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)); } } } } } } Loading
services/core/java/com/android/server/location/provider/AbstractLocationProvider.java +3 −17 Original line number Diff line number Diff line Loading @@ -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; } /** Loading @@ -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. */ Loading @@ -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. */ Loading