diff options
author | Soonil Nagarkar <sooniln@google.com> | 2021-05-20 18:28:55 -0700 |
---|---|---|
committer | Soonil Nagarkar <sooniln@google.com> | 2021-05-21 09:25:47 -0700 |
commit | 1ea818347e052f1ca0600013153e54a60db05bff (patch) | |
tree | ade3a609f3b8f77459bac94b6968609e03bc55fd | |
parent | f0ec6471cc7459cb31a394da902eb3ddc93395b7 (diff) |
Update location bypass allowlist
Create a new DeviceConfig entry to replace the prior Settings entry on
which the location ignore settings allowlist is based. This allows us to
allowlist based on attribution tag, and eliminate holes for large
applications.
Test: manual + CTS + GTS
Bug: 187421886
Change-Id: I31e61db79b93e202bd8c66efae1bb5aaf0c88ff5
25 files changed, 813 insertions, 205 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ff57a1916b47..0c3eed5d3d3d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4930,6 +4930,10 @@ package android.location { field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; } + public final class LocationDeviceConfig { + field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; + } + public class LocationManager { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); @@ -9088,6 +9092,7 @@ package android.provider { field public static final String NAMESPACE_GAME_DRIVER = "game_driver"; field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot"; field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention"; + field public static final String NAMESPACE_LOCATION = "location"; field public static final String NAMESPACE_MEDIA = "media"; field public static final String NAMESPACE_MEDIA_NATIVE = "media_native"; field public static final String NAMESPACE_NETD_NATIVE = "netd_native"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5a9dc827922f..12d7c8a77501 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1356,9 +1356,14 @@ package android.location { method public void setType(int); } + public final class LocationDeviceConfig { + field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; + } + public class LocationManager { method @NonNull public String[] getBackgroundThrottlingWhitelist(); - method @NonNull public String[] getIgnoreSettingsWhitelist(); + method @NonNull public android.os.PackageTagsList getIgnoreSettingsAllowlist(); + method @Deprecated @NonNull public String[] getIgnoreSettingsWhitelist(); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public java.util.List<java.lang.String> getProviderPackages(@NonNull String); } @@ -1653,6 +1658,28 @@ package android.os { method public void removeSyncBarrier(int); } + public final class PackageTagsList implements android.os.Parcelable { + method public boolean contains(@NonNull String, @Nullable String); + method public boolean contains(@NonNull android.os.PackageTagsList); + method public boolean containsAll(@NonNull String); + method public int describeContents(); + method public boolean includes(@NonNull String); + method public boolean isEmpty(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.os.PackageTagsList> CREATOR; + } + + public static final class PackageTagsList.Builder { + ctor public PackageTagsList.Builder(); + ctor public PackageTagsList.Builder(int); + method @NonNull public android.os.PackageTagsList.Builder add(@NonNull String); + method @NonNull public android.os.PackageTagsList.Builder add(@NonNull String, @Nullable String); + method @NonNull public android.os.PackageTagsList.Builder add(@NonNull android.os.PackageTagsList); + method @NonNull public android.os.PackageTagsList.Builder add(@NonNull java.util.Map<java.lang.String,? extends java.util.Set<java.lang.String>>); + method @NonNull public android.os.PackageTagsList build(); + method @NonNull public android.os.PackageTagsList.Builder clear(); + } + public final class Parcel { method public boolean allowSquashing(); method public int readExceptionCode(); @@ -2087,7 +2114,7 @@ package android.provider { field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; field public static final String HIDDEN_API_POLICY = "hidden_api_policy"; field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs"; - field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; + field @Deprecated public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; field public static final String LOW_POWER_MODE = "low_power"; field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; field @Deprecated public static final String NOTIFICATION_BUBBLES = "notification_bubbles"; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 53502d4e933f..02520afea147 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -49,6 +49,7 @@ import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; +import android.os.PackageTagsList; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -7407,7 +7408,7 @@ public class AppOpsManager { /** @hide */ public void setUserRestriction(int code, boolean restricted, IBinder token) { - setUserRestriction(code, restricted, token, (Map<String, String[]>) null); + setUserRestriction(code, restricted, token, null); } /** @@ -7415,7 +7416,7 @@ public class AppOpsManager { * @hide */ public void setUserRestriction(int code, boolean restricted, IBinder token, - @Nullable Map<String, String[]> excludedPackageTags) { + @Nullable PackageTagsList excludedPackageTags) { setUserRestrictionForUser(code, restricted, token, excludedPackageTags, mContext.getUserId()); } @@ -7425,7 +7426,7 @@ public class AppOpsManager { * @hide */ public void setUserRestrictionForUser(int code, boolean restricted, IBinder token, - @Nullable Map<String, String[]> excludedPackageTags, int userId) { + @Nullable PackageTagsList excludedPackageTags, int userId) { try { mService.setUserRestriction(code, restricted, token, userId, excludedPackageTags); } catch (RemoteException e) { diff --git a/core/java/android/os/PackageTagsList.aidl b/core/java/android/os/PackageTagsList.aidl new file mode 100644 index 000000000000..ac2c4e4623e6 --- /dev/null +++ b/core/java/android/os/PackageTagsList.aidl @@ -0,0 +1,18 @@ +/* Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.os; + +parcelable PackageTagsList; diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java new file mode 100644 index 000000000000..c94d3de33b6f --- /dev/null +++ b/core/java/android/os/PackageTagsList.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.TestApi; +import android.util.ArrayMap; +import android.util.ArraySet; + +import java.io.PrintWriter; +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. + * + * @hide + */ +@TestApi +public final class PackageTagsList implements Parcelable { + + // an empty set value matches any attribution tag + private final ArrayMap<String, ArraySet<String>> mPackageTags; + + private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) { + mPackageTags = Objects.requireNonNull(packageTags); + } + + /** + * Returns true if this instance is empty; + */ + public boolean isEmpty() { + return mPackageTags.isEmpty(); + } + + /** + * 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. + */ + public boolean includes(@NonNull String packageName) { + return mPackageTags.containsKey(packageName); + } + + /** + * Returns true if all attribution tags under the given package are contained within this + * instance. + */ + public boolean containsAll(@NonNull String packageName) { + Set<String> tags = mPackageTags.get(packageName); + return tags != null && tags.isEmpty(); + } + + /** + * Returns true if the given package and attribution tag are contained within this instance. + */ + public boolean contains(@NonNull String packageName, @Nullable String attributionTag) { + Set<String> tags = mPackageTags.get(packageName); + if (tags == null) { + return false; + } else if (tags.isEmpty()) { + return true; + } else { + return tags.contains(attributionTag); + } + } + + /** + * Returns true if the given PackageTagsList is a subset of this instance. + */ + public boolean contains(@NonNull PackageTagsList packageTagsList) { + int otherSize = packageTagsList.mPackageTags.size(); + if (otherSize > mPackageTags.size()) { + return false; + } + + for (int i = 0; i < otherSize; i++) { + String packageName = packageTagsList.mPackageTags.keyAt(i); + ArraySet<String> tags = mPackageTags.get(packageName); + if (tags == null) { + return false; + } + if (tags.isEmpty()) { + continue; + } + ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i); + if (otherTags.isEmpty()) { + return false; + } + if (!tags.containsAll(otherTags)) { + return false; + } + } + + return true; + } + + public static final @NonNull Parcelable.Creator<PackageTagsList> CREATOR = + new Parcelable.Creator<PackageTagsList>() { + @SuppressWarnings("unchecked") + @Override + public PackageTagsList createFromParcel(Parcel in) { + int count = in.readInt(); + ArrayMap<String, ArraySet<String>> packageTags = new ArrayMap<>(count); + for (int i = 0; i < count; i++) { + String key = in.readString8(); + ArraySet<String> value = (ArraySet<String>) in.readArraySet(null); + packageTags.append(key, value); + } + return new PackageTagsList(packageTags); + } + + @Override + public PackageTagsList[] newArray(int size) { + return new PackageTagsList[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + int count = mPackageTags.size(); + parcel.writeInt(count); + for (int i = 0; i < count; i++) { + parcel.writeString8(mPackageTags.keyAt(i)); + parcel.writeArraySet(mPackageTags.valueAt(i)); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PackageTagsList)) { + return false; + } + + PackageTagsList that = (PackageTagsList) o; + return mPackageTags.equals(that.mPackageTags); + } + + @Override + public int hashCode() { + return Objects.hash(mPackageTags); + } + + @Override + public @NonNull String toString() { + return mPackageTags.toString(); + } + + /** + * @hide + */ + public void dump(PrintWriter pw) { + int size = mPackageTags.size(); + for (int i = 0; i < size; i++) { + String packageName = mPackageTags.keyAt(i); + pw.print(packageName); + pw.print("["); + int tagsSize = mPackageTags.valueAt(i).size(); + if (tagsSize == 0) { + pw.print("*"); + } else { + for (int j = 0; j < tagsSize; j++) { + String attributionTag = mPackageTags.valueAt(i).valueAt(j); + if (j > 0) { + pw.print(", "); + } + if (attributionTag.startsWith(packageName)) { + pw.print(attributionTag.substring(packageName.length())); + } else { + pw.print(attributionTag); + } + } + } + pw.println("]"); + } + } + + /** + * Builder class for {@link PackageTagsList}. + */ + public static final class Builder { + + private final ArrayMap<String, ArraySet<String>> mPackageTags; + + /** + * Creates a new builder. + */ + public Builder() { + mPackageTags = new ArrayMap<>(); + } + + /** + * Creates a new builder with the given initial capacity. + */ + public Builder(int capacity) { + mPackageTags = new ArrayMap<>(capacity); + } + + /** + * Adds all attribution tags under the specified package to the builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder add(@NonNull String packageName) { + mPackageTags.computeIfAbsent(packageName, p -> new ArraySet<>()).clear(); + return this; + } + + /** + * Adds the specified package and attribution tag to the builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder add(@NonNull String packageName, @Nullable String attributionTag) { + ArraySet<String> tags = mPackageTags.get(packageName); + if (tags == null) { + tags = new ArraySet<>(1); + tags.add(attributionTag); + mPackageTags.put(packageName, tags); + } else if (!tags.isEmpty()) { + tags.add(attributionTag); + } + + return this; + } + + /** + * Adds the specified {@link PackageTagsList} to the builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder add(@NonNull PackageTagsList packageTagsList) { + return add(packageTagsList.mPackageTags); + } + + /** + * Adds 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. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder add(@NonNull Map<String, ? extends Set<String>> packageTagsMap) { + mPackageTags.ensureCapacity(packageTagsMap.size()); + for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) { + Set<String> newTags = entry.getValue(); + 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); + } + } + } + + return this; + } + + /** + * Clears the builder. + */ + public @NonNull Builder clear() { + mPackageTags.clear(); + return this; + } + + /** + * Constructs a new {@link PackageTagsList}. + */ + public @NonNull PackageTagsList build() { + return new PackageTagsList(copy(mPackageTags)); + } + + private static ArrayMap<String, ArraySet<String>> copy( + ArrayMap<String, ArraySet<String>> value) { + int size = value.size(); + ArrayMap<String, ArraySet<String>> copy = new ArrayMap<>(size); + for (int i = 0; i < size; i++) { + String packageName = value.keyAt(i); + ArraySet<String> tags = new ArraySet<>(Objects.requireNonNull(value.valueAt(i))); + copy.append(packageName, tags); + } + return copy; + } + } +} diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 3daa3a545a1d..0c0d70c449a9 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -258,6 +258,14 @@ public final class DeviceConfig { public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler"; /** + * Namespace for all location related features. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_LOCATION = "location"; + + /** * Namespace for all media related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0c23ae6b4b29..5cfb665745a8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10988,10 +10988,12 @@ public final class Settings { /** * Packages that are whitelisted for ignoring location settings (may retrieve location even * when user location settings are off), for emergency purposes. + * @deprecated No longer used from Android 12+ * @hide */ @TestApi @Readable + @Deprecated public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 3cf46214fbec..c112d09d40e4 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -23,6 +23,7 @@ import android.app.RuntimeAppOpAccessMessage; import android.content.AttributionSource; import android.content.pm.ParceledListSlice; import android.os.Bundle; +import android.os.PackageTagsList; import android.os.RemoteCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsActiveCallback; @@ -92,7 +93,7 @@ interface IAppOpsService { void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages); void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle); - void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in Map<String, String[]> excludedPackageTags); + void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in PackageTagsList excludedPackageTags); void removeUser(int userHandle); void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback); diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index a9b47aab4d18..bd0de2984b7f 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -169,7 +169,7 @@ public class SystemConfig { // These are the packages that are white-listed to be able to retrieve location even when user // location settings are off, for emergency purposes, as read from the configuration files. - final ArraySet<String> mAllowIgnoreLocationSettings = new ArraySet<>(); + final ArrayMap<String, ArraySet<String>> mAllowIgnoreLocationSettings = new ArrayMap<>(); // These are the action strings of broadcasts which are whitelisted to // be delivered anonymously even to apps which target O+. @@ -313,7 +313,7 @@ public class SystemConfig { return mAllowUnthrottledLocation; } - public ArraySet<String> getAllowIgnoreLocationSettings() { + public ArrayMap<String, ArraySet<String>> getAllowIgnoreLocationSettings() { return mAllowIgnoreLocationSettings; } @@ -867,11 +867,25 @@ public class SystemConfig { case "allow-ignore-location-settings": { if (allowOverrideAppRestrictions) { String pkgname = parser.getAttributeValue(null, "package"); + String attributionTag = parser.getAttributeValue(null, + "attributionTag"); if (pkgname == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { - mAllowIgnoreLocationSettings.add(pkgname); + ArraySet<String> tags = mAllowIgnoreLocationSettings.get(pkgname); + if (tags == null || !tags.isEmpty()) { + if (tags == null) { + tags = new ArraySet<>(1); + mAllowIgnoreLocationSettings.put(pkgname, tags); + } + if (!"*".equals(attributionTag)) { + if ("null".equals(attributionTag)) { + attributionTag = null; + } + tags.add(attributionTag); + } + } } } else { logNotAllowedInPartition(name, permFile, parser); diff --git a/core/tests/coretests/src/android/os/PackageTagsListTest.java b/core/tests/coretests/src/android/os/PackageTagsListTest.java new file mode 100644 index 000000000000..518e02e44b06 --- /dev/null +++ b/core/tests/coretests/src/android/os/PackageTagsListTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; +import android.util.ArrayMap; +import android.util.ArraySet; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class PackageTagsListTest { + + @Test + public void testPackageTagsList() { + PackageTagsList.Builder builder = new PackageTagsList.Builder() + .add("package1", "attr1") + .add("package1", "attr2") + .add("package2"); + PackageTagsList list = builder.build(); + + assertTrue(list.contains(builder.build())); + assertTrue(list.contains("package1", "attr1")); + assertTrue(list.contains("package1", "attr2")); + assertTrue(list.contains("package2", "attr1")); + assertTrue(list.contains("package2", "attr2")); + assertTrue(list.contains("package2", "attr3")); + assertTrue(list.containsAll("package2")); + assertTrue(list.includes("package1")); + assertTrue(list.includes("package2")); + assertFalse(list.contains("package1", "attr3")); + assertFalse(list.containsAll("package1")); + assertFalse(list.includes("package3")); + + PackageTagsList bigList = builder.add("package3").build(); + assertTrue(bigList.contains(builder.build())); + assertTrue(bigList.contains(list)); + assertFalse(list.contains(bigList)); + } + + @Test + public void testPackageTagsList_BuildFromMap() { + ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); + map.put("package1", new ArraySet<>(Arrays.asList("attr1", "attr2"))); + map.put("package2", new ArraySet<>()); + + PackageTagsList.Builder builder = new PackageTagsList.Builder().add(map); + PackageTagsList list = builder.build(); + + assertTrue(list.contains(builder.build())); + assertTrue(list.contains("package1", "attr1")); + assertTrue(list.contains("package1", "attr2")); + assertTrue(list.contains("package2", "attr1")); + assertTrue(list.contains("package2", "attr2")); + assertTrue(list.contains("package2", "attr3")); + assertTrue(list.containsAll("package2")); + assertTrue(list.includes("package1")); + assertTrue(list.includes("package2")); + assertFalse(list.contains("package1", "attr3")); + assertFalse(list.containsAll("package1")); + assertFalse(list.includes("package3")); + + map.put("package3", new ArraySet<>()); + PackageTagsList bigList = builder.add(map).build(); + assertTrue(bigList.contains(builder.build())); + assertTrue(bigList.contains(list)); + assertFalse(list.contains(bigList)); + } + + @Test + public void testWriteToParcel() { + PackageTagsList list = new PackageTagsList.Builder() + .add("package1", "attr1") + .add("package1", "attr2") + .add("package2") + .build(); + Parcel parcel = Parcel.obtain(); + list.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + PackageTagsList newList = PackageTagsList.CREATOR.createFromParcel(parcel); + parcel.recycle(); + + assertEquals(list, newList); + } +} diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd index 543504764ee3..0ec8f7d4f2ae 100644 --- a/core/xsd/permission.xsd +++ b/core/xsd/permission.xsd @@ -98,6 +98,7 @@ </xs:complexType> <xs:complexType name="allow-ignore-location-settings"> <xs:attribute name="package" type="xs:string"/> + <xs:attribute name="attributionTag" type="xs:string"/> </xs:complexType> <xs:complexType name="allow-implicit-broadcast"> <xs:attribute name="action" type="xs:string"/> diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt index c36c422a852d..f3beea1b96a9 100644 --- a/core/xsd/schema/current.txt +++ b/core/xsd/schema/current.txt @@ -11,7 +11,9 @@ package com.android.xml.permission.configfile { public class AllowIgnoreLocationSettings { ctor public AllowIgnoreLocationSettings(); + method public String getAttributionTag(); method public String get_package(); + method public void setAttributionTag(String); method public void set_package(String); } diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 6fa6536997c4..c9e4e0a9cb92 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -41,6 +41,7 @@ import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.os.Bundle; import android.os.ICancellationSignal; +import android.os.PackageTagsList; /** * System private API for talking with the location service. @@ -133,5 +134,5 @@ interface ILocationManager // used by gts tests to verify whitelists String[] getBackgroundThrottlingWhitelist(); - String[] getIgnoreSettingsWhitelist(); + PackageTagsList getIgnoreSettingsAllowlist(); } diff --git a/location/java/android/location/LocationDeviceConfig.java b/location/java/android/location/LocationDeviceConfig.java new file mode 100644 index 000000000000..92845745828b --- /dev/null +++ b/location/java/android/location/LocationDeviceConfig.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.annotation.SystemApi; +import android.annotation.TestApi; + +/** + * DeviceConfig keys within the location namespace. + * + * @hide + */ +@SystemApi +@TestApi +public final class LocationDeviceConfig { + + /** + * Package/tag combinations that are allowedlisted for ignoring location settings (may retrieve + * location even when user location settings are off, and may ignore throttling, etc), for + * emergency purposes only. + * + * <p>Package/tag combinations are separated by commas (","), and with in each combination is a + * package name followed by 0 or more attribution tags, separated by semicolons (";"). If a + * package is followed by 0 attribution tags, this is interpreted the same as the wildcard + * value. There are two special interpreted values for attribution tags, the wildcard value + * ("*") which represents all attribution tags, and the null value ("null"), which is converted + * to the null string (since attribution tags may be null). This format implies that attribution + * tags which should be on this list may not contain semicolons. + * + * <p>Examples of valid entries: + * + * <ul> + * <li>android</li> + * <li>android;*</li> + * <li>android;*,com.example.app;null;my_attr</li> + * <li>android;*,com.example.app;null;my_attr,com.example.otherapp;my_attr</li> + * </ul> + */ + public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; + + private LocationDeviceConfig() {} +} diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 7c2f5408fffb..f83dc407d870 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -58,6 +58,7 @@ import android.os.HandlerExecutor; import android.os.ICancellationSignal; import android.os.IRemoteCallback; import android.os.Looper; +import android.os.PackageTagsList; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -447,12 +448,23 @@ public class LocationManager { } /** + * @deprecated Do not use. * @hide */ + @Deprecated @TestApi public @NonNull String[] getIgnoreSettingsWhitelist() { + return new String[0]; + } + + /** + * For testing purposes only. + * @hide + */ + @TestApi + public @NonNull PackageTagsList getIgnoreSettingsAllowlist() { try { - return mService.getIgnoreSettingsWhitelist(); + return mService.getIgnoreSettingsAllowlist(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 2f54e2124247..e5eecb2068e0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -896,9 +896,6 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS, GlobalSettingsProto.Location.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS); - dumpSetting(s, p, - Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, - GlobalSettingsProto.Location.IGNORE_SETTINGS_PACKAGE_WHITELIST); p.end(locationToken); final long lpmToken = p.start(GlobalSettingsProto.LOW_POWER_MODE); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 3a824345c8a2..f538875bf337 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -321,7 +321,6 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, - Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, Settings.Global.LOCK_SOUND, Settings.Global.LOOPER_STATS, diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c5582a036549..541dcdc94a51 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -116,6 +116,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.PackageTagsList; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallbackList; @@ -132,6 +133,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; import android.util.LongSparseArray; import android.util.Pair; @@ -6194,17 +6196,19 @@ public class AppOpsService extends IAppOpsService.Stub { final int excludedPackageCount = restrictionState.perUserExcludedPackageTags != null ? restrictionState.perUserExcludedPackageTags.size() : 0; if (excludedPackageCount > 0 && dumpOp < 0) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + ipw.increaseIndent(); boolean printedPackagesHeader = false; for (int j = 0; j < excludedPackageCount; j++) { int userId = restrictionState.perUserExcludedPackageTags.keyAt(j); - Map<String, String[]> packageNames = + PackageTagsList packageNames = restrictionState.perUserExcludedPackageTags.valueAt(j); if (packageNames == null) { continue; } boolean hasPackage; if (dumpPackage != null) { - hasPackage = packageNames.containsKey(dumpPackage); + hasPackage = packageNames.includes(dumpPackage); } else { hasPackage = true; } @@ -6212,32 +6216,29 @@ public class AppOpsService extends IAppOpsService.Stub { continue; } if (!printedTokenHeader) { - pw.println(" User restrictions for token " + token + ":"); + ipw.println("User restrictions for token " + token + ":"); printedTokenHeader = true; } + + ipw.increaseIndent(); if (!printedPackagesHeader) { - pw.println(" Excluded packages:"); + ipw.println("Excluded packages:"); printedPackagesHeader = true; } - pw.print(" "); - pw.print("user: "); - pw.print(userId); - pw.println(" packages: "); - for (Map.Entry<String, String[]> entry : packageNames.entrySet()) { - if (entry.getValue() == null) { - continue; - } - pw.print(" "); - pw.print(entry.getKey()); - pw.print(": "); - if (entry.getValue().length == 0) { - pw.print("*"); - } else { - pw.print(Arrays.toString(entry.getValue())); - } - pw.println(); - } + + ipw.increaseIndent(); + ipw.print("user: "); + ipw.print(userId); + ipw.println(" packages: "); + + ipw.increaseIndent(); + packageNames.dump(ipw); + + ipw.decreaseIndent(); + ipw.decreaseIndent(); + ipw.decreaseIndent(); } + ipw.decreaseIndent(); } } } @@ -6270,7 +6271,7 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, - Map<String, String[]> excludedPackageTags) { + PackageTagsList excludedPackageTags) { if (Binder.getCallingPid() != Process.myPid()) { mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS, Binder.getCallingPid(), Binder.getCallingUid(), null); @@ -6290,7 +6291,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, - int userHandle, Map<String, String[]> excludedPackageTags) { + int userHandle, PackageTagsList excludedPackageTags) { synchronized (AppOpsService.this) { ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); @@ -6835,7 +6836,7 @@ public class AppOpsService extends IAppOpsService.Stub { private final class ClientRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; - SparseArray<Map<String, String[]>> perUserExcludedPackageTags; + SparseArray<PackageTagsList> perUserExcludedPackageTags; public ClientRestrictionState(IBinder token) throws RemoteException { @@ -6844,7 +6845,7 @@ public class AppOpsService extends IAppOpsService.Stub { } public boolean setRestriction(int code, boolean restricted, - Map<String, String[]> excludedPackageTags, int userId) { + PackageTagsList excludedPackageTags, int userId) { boolean changed = false; if (perUserRestrictions == null && restricted) { @@ -6886,7 +6887,8 @@ public class AppOpsService extends IAppOpsService.Stub { } if (userRestrictions != null) { - final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackageTags); + final boolean noExcludedPackages = + excludedPackageTags == null || excludedPackageTags.isEmpty(); if (perUserExcludedPackageTags == null && !noExcludedPackages) { perUserExcludedPackageTags = new SparseArray<>(); } @@ -6897,16 +6899,7 @@ public class AppOpsService extends IAppOpsService.Stub { perUserExcludedPackageTags = null; } } else { - Map<String, String[]> userExcludedPackageTags = - perUserExcludedPackageTags.get(thisUserId); - if (userExcludedPackageTags == null) { - userExcludedPackageTags = new ArrayMap<>( - excludedPackageTags.size()); - perUserExcludedPackageTags.put(thisUserId, - userExcludedPackageTags); - } - userExcludedPackageTags.clear(); - userExcludedPackageTags.putAll(excludedPackageTags); + perUserExcludedPackageTags.put(thisUserId, excludedPackageTags); } changed = true; } @@ -6932,19 +6925,11 @@ public class AppOpsService extends IAppOpsService.Stub { if (perUserExcludedPackageTags == null) { return true; } - Map<String, String[]> perUserExclusions = perUserExcludedPackageTags.get(userId); + PackageTagsList perUserExclusions = perUserExcludedPackageTags.get(userId); if (perUserExclusions == null) { return true; } - String[] excludedTags = perUserExclusions.get(packageName); - if (excludedTags == null) { - return true; - } - if (excludedTags.length == 0) { - // all attribution tags within the package are excluded - return false; - } - return !ArrayUtils.contains(excludedTags, attributionTag); + return !perUserExclusions.contains(packageName, attributionTag); } public void removeUser(int userId) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 634b6aa73539..b9e97e89051b 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -76,6 +76,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.ICancellationSignal; +import android.os.PackageTagsList; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; @@ -139,7 +140,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; @@ -271,7 +271,7 @@ public class LocationManagerService extends ILocationManager.Stub implements mInjector.getSettingsHelper().addOnLocationEnabledChangedListener( this::onLocationModeChanged); - mInjector.getSettingsHelper().addOnIgnoreSettingsPackageWhitelistChangedListener( + mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener( () -> refreshAppOpsRestrictions(UserHandle.USER_ALL)); mInjector.getUserInfoHelper().addListener((userId, change) -> { if (change == UserInfoHelper.UserListener.USER_STARTED) { @@ -641,9 +641,8 @@ public class LocationManagerService extends ILocationManager.Stub implements } @Override - public String[] getIgnoreSettingsWhitelist() { - return mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist().toArray( - new String[0]); + public PackageTagsList getIgnoreSettingsAllowlist() { + return mInjector.getSettingsHelper().getIgnoreSettingsAllowlist(); } @Nullable @@ -1408,26 +1407,17 @@ public class LocationManagerService extends ILocationManager.Stub implements boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId); - ArrayMap<String, String[]> allowedPackages = null; + PackageTagsList allowedPackages = null; if (!enabled) { - ArrayMap<String, ArraySet<String>> packages = new ArrayMap<>(); + PackageTagsList.Builder builder = new PackageTagsList.Builder(); for (LocationProviderManager manager : mProviderManagers) { CallerIdentity identity = manager.getIdentity(); if (identity != null) { - packages.computeIfAbsent(identity.getPackageName(), k -> new ArraySet<>()).add( - identity.getAttributionTag()); + builder.add(identity.getPackageName(), identity.getAttributionTag()); } } - for (String packageName : - mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist()) { - packages.computeIfAbsent(packageName, k -> new ArraySet<>()).clear(); - } - packages.computeIfAbsent(mContext.getPackageName(), k -> new ArraySet<>()).clear(); - - allowedPackages = new ArrayMap<>(); - for (Map.Entry<String, ArraySet<String>> entry : packages.entrySet()) { - allowedPackages.put(entry.getKey(), entry.getValue().toArray(new String[0])); - } + builder.add(mInjector.getSettingsHelper().getIgnoreSettingsAllowlist()); + allowedPackages = builder.build(); } AppOpsManager appOpsManager = Objects.requireNonNull( diff --git a/services/core/java/com/android/server/location/injector/SettingsHelper.java b/services/core/java/com/android/server/location/injector/SettingsHelper.java index 387ab77ebe3a..148afa75ca75 100644 --- a/services/core/java/com/android/server/location/injector/SettingsHelper.java +++ b/services/core/java/com/android/server/location/injector/SettingsHelper.java @@ -16,6 +16,7 @@ package com.android.server.location.injector; +import android.os.PackageTagsList; import android.util.IndentingPrintWriter; import java.io.FileDescriptor; @@ -146,21 +147,21 @@ public abstract class SettingsHelper { GlobalSettingChangedListener listener); /** - * Retrieve the ignore settings package whitelist. + * Retrieve the ignore location settings package+tags allowlist setting. */ - public abstract Set<String> getIgnoreSettingsPackageWhitelist(); + public abstract PackageTagsList getIgnoreSettingsAllowlist(); /** * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an * unspecified thread. */ - public abstract void addOnIgnoreSettingsPackageWhitelistChangedListener( + public abstract void addIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener); /** * Remove a listener for changes to the ignore settings package whitelist. */ - public abstract void removeOnIgnoreSettingsPackageWhitelistChangedListener( + public abstract void removeIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener); /** diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java index 632ed6ef192a..c315da476d99 100644 --- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java @@ -16,11 +16,11 @@ package com.android.server.location.injector; +import static android.location.LocationDeviceConfig.IGNORE_SETTINGS_ALLOWLIST; import static android.provider.Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST; import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS; -import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST; import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M; import static android.provider.Settings.Secure.LOCATION_MODE; import static android.provider.Settings.Secure.LOCATION_MODE_OFF; @@ -35,10 +35,13 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Handler; +import android.os.PackageTagsList; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; @@ -77,7 +80,7 @@ public class SystemSettingsHelper extends SettingsHelper { private final StringListCachedSecureSetting mLocationPackageBlacklist; private final StringListCachedSecureSetting mLocationPackageWhitelist; private final StringSetCachedGlobalSetting mBackgroundThrottlePackageWhitelist; - private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist; + private final PackageTagsListSetting mIgnoreSettingsPackageAllowlist; public SystemSettingsHelper(Context context) { mContext = context; @@ -95,10 +98,9 @@ public class SystemSettingsHelper extends SettingsHelper { LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), FgThread.getHandler()); - mIgnoreSettingsPackageWhitelist = new StringSetCachedGlobalSetting(context, - LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, - () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), - FgThread.getHandler()); + mIgnoreSettingsPackageAllowlist = new PackageTagsListSetting( + IGNORE_SETTINGS_ALLOWLIST, + () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings()); } /** Called when system is ready. */ @@ -108,20 +110,14 @@ public class SystemSettingsHelper extends SettingsHelper { mLocationPackageBlacklist.register(); mLocationPackageWhitelist.register(); mBackgroundThrottlePackageWhitelist.register(); - mIgnoreSettingsPackageWhitelist.register(); + mIgnoreSettingsPackageAllowlist.register(); } - /** - * Retrieve if location is enabled or not. - */ @Override public boolean isLocationEnabled(int userId) { return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF; } - /** - * Set location enabled for a user. - */ @Override public void setLocationEnabled(boolean enabled, int userId) { final long identity = Binder.clearCallingIdentity(); @@ -138,53 +134,33 @@ public class SystemSettingsHelper extends SettingsHelper { } } - /** - * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified - * thread. - */ @Override public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) { mLocationMode.addListener(listener); } - /** - * Remove a listener for changes to the location enabled setting. - */ @Override public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) { mLocationMode.removeListener(listener); } - /** - * Retrieve the background throttle interval. - */ @Override public long getBackgroundThrottleIntervalMs() { return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS); } - /** - * Add a listener for changes to the background throttle interval. Callbacks occur on an - * unspecified thread. - */ @Override public void addOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener) { mBackgroundThrottleIntervalMs.addListener(listener); } - /** - * Remove a listener for changes to the background throttle interval. - */ @Override public void removeOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener) { mBackgroundThrottleIntervalMs.removeListener(listener); } - /** - * Check if the given package is blacklisted for location access. - */ @Override public boolean isLocationPackageBlacklisted(int userId, String packageName) { List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId); @@ -208,10 +184,6 @@ public class SystemSettingsHelper extends SettingsHelper { return false; } - /** - * Add a listener for changes to the location package blacklist. Callbacks occur on an - * unspecified thread. - */ @Override public void addOnLocationPackageBlacklistChangedListener( UserSettingChangedListener listener) { @@ -219,9 +191,6 @@ public class SystemSettingsHelper extends SettingsHelper { mLocationPackageWhitelist.addListener(listener); } - /** - * Remove a listener for changes to the location package blacklist. - */ @Override public void removeOnLocationPackageBlacklistChangedListener( UserSettingChangedListener listener) { @@ -229,90 +198,57 @@ public class SystemSettingsHelper extends SettingsHelper { mLocationPackageWhitelist.removeListener(listener); } - /** - * Retrieve the background throttle package whitelist. - */ @Override public Set<String> getBackgroundThrottlePackageWhitelist() { return mBackgroundThrottlePackageWhitelist.getValue(); } - /** - * Add a listener for changes to the background throttle package whitelist. Callbacks occur on - * an unspecified thread. - */ @Override public void addOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener) { mBackgroundThrottlePackageWhitelist.addListener(listener); } - /** - * Remove a listener for changes to the background throttle package whitelist. - */ @Override public void removeOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener) { mBackgroundThrottlePackageWhitelist.removeListener(listener); } - /** - * Retrieve the gnss measurements full tracking enabled setting. - */ @Override public boolean isGnssMeasurementsFullTrackingEnabled() { return mGnssMeasurementFullTracking.getValue(false); } - /** - * Add a listener for changes to the background throttle package whitelist. Callbacks occur on - * an unspecified thread. - */ @Override public void addOnGnssMeasurementsFullTrackingEnabledChangedListener( GlobalSettingChangedListener listener) { mGnssMeasurementFullTracking.addListener(listener); } - /** - * Remove a listener for changes to the background throttle package whitelist. - */ @Override public void removeOnGnssMeasurementsFullTrackingEnabledChangedListener( GlobalSettingChangedListener listener) { mGnssMeasurementFullTracking.removeListener(listener); } - /** - * Retrieve the ignore settings package whitelist. - */ @Override - public Set<String> getIgnoreSettingsPackageWhitelist() { - return mIgnoreSettingsPackageWhitelist.getValue(); + public PackageTagsList getIgnoreSettingsAllowlist() { + return mIgnoreSettingsPackageAllowlist.getValue(); } - /** - * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an - * unspecified thread. - */ @Override - public void addOnIgnoreSettingsPackageWhitelistChangedListener( + public void addIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener) { - mIgnoreSettingsPackageWhitelist.addListener(listener); + mIgnoreSettingsPackageAllowlist.addListener(listener); } - /** - * Remove a listener for changes to the ignore settings package whitelist. - */ @Override - public void removeOnIgnoreSettingsPackageWhitelistChangedListener( + public void removeIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener) { - mIgnoreSettingsPackageWhitelist.removeListener(listener); + mIgnoreSettingsPackageAllowlist.removeListener(listener); } - /** - * Retrieve the background throttling proximity alert interval. - */ @Override public long getBackgroundThrottleProximityAlertIntervalMs() { final long identity = Binder.clearCallingIdentity(); @@ -325,10 +261,6 @@ public class SystemSettingsHelper extends SettingsHelper { } } - /** - * Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid - * coarsening. - */ @Override public float getCoarseLocationAccuracyM() { final long identity = Binder.clearCallingIdentity(); @@ -344,9 +276,6 @@ public class SystemSettingsHelper extends SettingsHelper { } } - /** - * Dump info for debugging. - */ @Override public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { int[] userIds; @@ -428,13 +357,11 @@ public class SystemSettingsHelper extends SettingsHelper { ipw.decreaseIndent(); } - Set<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue(); - if (!ignoreSettingsPackageWhitelist.isEmpty()) { + PackageTagsList ignoreSettingsAllowlist = mIgnoreSettingsPackageAllowlist.getValue(); + if (!ignoreSettingsAllowlist.isEmpty()) { ipw.println("Bypass Allow Packages:"); ipw.increaseIndent(); - for (String packageName : ignoreSettingsPackageWhitelist) { - ipw.println(packageName); - } + ignoreSettingsAllowlist.dump(ipw); ipw.decreaseIndent(); } } @@ -687,4 +614,139 @@ public class SystemSettingsHelper extends SettingsHelper { super.onChange(selfChange, uri, userId); } } + + private static class DeviceConfigSetting implements DeviceConfig.OnPropertiesChangedListener { + + protected final String mName; + private final CopyOnWriteArrayList<GlobalSettingChangedListener> mListeners; + + @GuardedBy("this") + private boolean mRegistered; + + DeviceConfigSetting(String name) { + mName = name; + mListeners = new CopyOnWriteArrayList<>(); + } + + protected synchronized boolean isRegistered() { + return mRegistered; + } + + protected synchronized void register() { + if (mRegistered) { + return; + } + + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LOCATION, + FgThread.getExecutor(), this); + mRegistered = true; + } + + public void addListener(GlobalSettingChangedListener listener) { + mListeners.add(listener); + } + + public void removeListener(GlobalSettingChangedListener listener) { + mListeners.remove(listener); + } + + @Override + public final void onPropertiesChanged(DeviceConfig.Properties properties) { + if (!properties.getKeyset().contains(mName)) { + return; + } + + onPropertiesChanged(); + } + + public void onPropertiesChanged() { + if (D) { + Log.d(TAG, "location device config setting changed: " + mName); + } + + for (UserSettingChangedListener listener : mListeners) { + listener.onSettingChanged(UserHandle.USER_ALL); + } + } + } + + + + private static class PackageTagsListSetting extends DeviceConfigSetting { + + private final Supplier<ArrayMap<String, ArraySet<String>>> mBaseValuesSupplier; + + @GuardedBy("this") + private boolean mValid; + @GuardedBy("this") + private PackageTagsList mCachedValue; + + PackageTagsListSetting(String name, + Supplier<ArrayMap<String, ArraySet<String>>> baseValuesSupplier) { + super(name); + mBaseValuesSupplier = baseValuesSupplier; + } + + public synchronized PackageTagsList getValue() { + PackageTagsList value = mCachedValue; + if (!mValid) { + final long identity = Binder.clearCallingIdentity(); + try { + PackageTagsList.Builder builder = new PackageTagsList.Builder().add( + mBaseValuesSupplier.get()); + + String setting = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_LOCATION, + mName); + if (!TextUtils.isEmpty(setting)) { + for (String packageAndTags : setting.split(",")) { + if (TextUtils.isEmpty(packageAndTags)) { + continue; + } + + String[] packageThenTags = packageAndTags.split(";"); + String packageName = packageThenTags[0]; + if (packageThenTags.length == 1) { + builder.add(packageName); + } else { + for (int i = 1; i < packageThenTags.length; i++) { + String attributionTag = packageThenTags[i]; + if ("null".equals(attributionTag)) { + attributionTag = null; + } + + if ("*".equals(attributionTag)) { + builder.add(packageName); + } else { + builder.add(packageName, attributionTag); + } + } + } + } + } + + value = builder.build(); + } finally { + Binder.restoreCallingIdentity(identity); + } + + if (isRegistered()) { + mValid = true; + mCachedValue = value; + } + } + + return value; + } + + public synchronized void invalidate() { + mValid = false; + mCachedValue = null; + } + + @Override + public void onPropertiesChanged() { + invalidate(); + super.onPropertiesChanged(); + } + } } diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index a4a59564d0d1..6d7f79250715 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -613,9 +613,9 @@ public class LocationProviderManager extends boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored(); if (locationSettingsIgnored) { // if we are not currently allowed use location settings ignored, disable it - if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( - getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider( - null, getIdentity())) { + if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains( + getIdentity().getPackageName(), getIdentity().getAttributionTag()) + && !mLocationManagerInternal.isProvider(null, getIdentity())) { builder.setLocationSettingsIgnored(false); locationSettingsIgnored = false; } @@ -1820,7 +1820,7 @@ public class LocationProviderManager extends mBackgroundThrottlePackageWhitelistChangedListener); mSettingsHelper.addOnLocationPackageBlacklistChangedListener( mLocationPackageBlacklistChangedListener); - mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener( + mSettingsHelper.addIgnoreSettingsAllowlistChangedListener( mIgnoreSettingsPackageWhitelistChangedListener); mLocationPermissionsHelper.addListener(mLocationPermissionsListener); mAppForegroundHelper.addListener(mAppForegroundChangedListener); @@ -1841,7 +1841,7 @@ public class LocationProviderManager extends mBackgroundThrottlePackageWhitelistChangedListener); mSettingsHelper.removeOnLocationPackageBlacklistChangedListener( mLocationPackageBlacklistChangedListener); - mSettingsHelper.removeOnIgnoreSettingsPackageWhitelistChangedListener( + mSettingsHelper.removeIgnoreSettingsAllowlistChangedListener( mIgnoreSettingsPackageWhitelistChangedListener); mLocationPermissionsHelper.removeListener(mLocationPermissionsListener); mAppForegroundHelper.removeListener(mAppForegroundChangedListener); diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 2ac50b63cfc6..b296ef2a1443 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -42,6 +42,7 @@ import android.os.IBinder; import android.os.IInterface; import android.os.Looper; import android.os.Message; +import android.os.PackageTagsList; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; @@ -85,7 +86,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -856,14 +856,13 @@ public class VrManagerService extends SystemService // If user changed drop restrictions for the old user. if (oldUserId != newUserId) { appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - false, mOverlayToken, (Map<String, String[]>) null, oldUserId); + false, mOverlayToken, null, oldUserId); } // Apply the restrictions for the current user based on vr state - ArrayMap<String, String[]> exemptions = null; + PackageTagsList exemptions = null; if (exemptedPackage != null) { - exemptions = new ArrayMap<>(1); - exemptions.put(exemptedPackage, new String[0]); + exemptions = new PackageTagsList.Builder(1).add(exemptedPackage).build(); } appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java index 2e101070f8b4..f1099f0e8184 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java @@ -16,6 +16,7 @@ package com.android.server.location.injector; +import android.os.PackageTagsList; import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.SparseArray; @@ -40,7 +41,7 @@ public class FakeSettingsHelper extends SettingsHelper { private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners; - private Setting(Object defaultValue) { + Setting(Object defaultValue) { mValues = new SparseArray<>(); mDefaultValue = defaultValue; mListeners = new CopyOnWriteArrayList<>(); @@ -83,7 +84,8 @@ public class FakeSettingsHelper extends SettingsHelper { private final Setting mBackgroundThrottlePackageWhitelistSetting = new Setting( Collections.emptySet()); private final Setting mGnssMeasurementsFullTrackingSetting = new Setting(Boolean.FALSE); - private final Setting mIgnoreSettingsPackageWhitelist = new Setting(Collections.emptySet()); + private final Setting mIgnoreSettingsAllowlist = new Setting( + new PackageTagsList.Builder().build()); private final Setting mBackgroundThrottleProximityAlertIntervalSetting = new Setting( 30 * 60 * 1000L); private final Setting mCoarseLocationAccuracySetting = new Setting(2000.0f); @@ -192,24 +194,24 @@ public class FakeSettingsHelper extends SettingsHelper { } @Override - public Set<String> getIgnoreSettingsPackageWhitelist() { - return mIgnoreSettingsPackageWhitelist.getValue(Set.class); + public PackageTagsList getIgnoreSettingsAllowlist() { + return mIgnoreSettingsAllowlist.getValue(PackageTagsList.class); } - public void setIgnoreSettingsPackageWhitelist(Set<String> newValue) { - mIgnoreSettingsPackageWhitelist.setValue(newValue); + public void setIgnoreSettingsAllowlist(PackageTagsList newValue) { + mIgnoreSettingsAllowlist.setValue(newValue); } @Override - public void addOnIgnoreSettingsPackageWhitelistChangedListener( + public void addIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener) { - mIgnoreSettingsPackageWhitelist.addListener(listener); + mIgnoreSettingsAllowlist.addListener(listener); } @Override - public void removeOnIgnoreSettingsPackageWhitelistChangedListener( + public void removeIgnoreSettingsAllowlistChangedListener( GlobalSettingChangedListener listener) { - mIgnoreSettingsPackageWhitelist.removeListener(listener); + mIgnoreSettingsAllowlist.removeListener(listener); } @Override diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 00246dd21ed9..6bc3b6041070 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -71,6 +71,7 @@ import android.location.util.identity.CallerIdentity; import android.os.Bundle; import android.os.ICancellationSignal; import android.os.IRemoteCallback; +import android.os.PackageTagsList; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -923,8 +924,9 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_IgnoreLocationSettings() { - mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( - Collections.singleton(IDENTITY.getPackageName())); + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().add( + IDENTITY.getPackageName()).build()); ILocationListener listener1 = createMockLocationListener(); LocationRequest request1 = new LocationRequest.Builder(5) @@ -950,8 +952,9 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_IgnoreLocationSettings_ProviderDisabled() { - mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( - Collections.singleton(IDENTITY.getPackageName())); + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().add( + IDENTITY.getPackageName()).build()); ILocationListener listener1 = createMockLocationListener(); LocationRequest request1 = new LocationRequest.Builder(1) @@ -975,8 +978,9 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_IgnoreLocationSettings_NoAllowlist() { - mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( - Collections.singleton(IDENTITY.getPackageName())); + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().add( + IDENTITY.getPackageName()).build()); ILocationListener listener = createMockLocationListener(); LocationRequest request = new LocationRequest.Builder(1) @@ -985,7 +989,8 @@ public class LocationProviderManagerTest { .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); - mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet()); + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().build()); assertThat(mProvider.getRequest().isActive()).isTrue(); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1); @@ -994,8 +999,9 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_BackgroundThrottle_IgnoreLocationSettings() { - mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist( - Collections.singleton(IDENTITY.getPackageName())); + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().add( + IDENTITY.getPackageName()).build()); ILocationListener listener1 = createMockLocationListener(); LocationRequest request1 = new LocationRequest.Builder(5) |