Merge "Fix bugs in LocationManagerService and AppOpsPolicy" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
a244ceeb2e
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -158,7 +158,7 @@ public class MockableLocationProviderTest {
|
||||
|
||||
@Test
|
||||
public void testSetState() {
|
||||
assertThat(mProvider.isAllowed()).isFalse();
|
||||
assertThat(mProvider.getState().allowed).isFalse();
|
||||
|
||||
AbstractLocationProvider.State newState;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user