diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ff57a1916b47e..0c3eed5d3d3d6 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 5a9dc827922fd..12d7c8a775010 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 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 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>); + 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 53502d4e933f7..02520afea1479 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) 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 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 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 0000000000000..ac2c4e4623e60 --- /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 0000000000000..c94d3de33b6f3 --- /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> mPackageTags; + + private PackageTagsList(@NonNull ArrayMap> 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 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 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 tags = mPackageTags.get(packageName); + if (tags == null) { + return false; + } + if (tags.isEmpty()) { + continue; + } + ArraySet otherTags = packageTagsList.mPackageTags.valueAt(i); + if (otherTags.isEmpty()) { + return false; + } + if (!tags.containsAll(otherTags)) { + return false; + } + } + + return true; + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @SuppressWarnings("unchecked") + @Override + public PackageTagsList createFromParcel(Parcel in) { + int count = in.readInt(); + ArrayMap> packageTags = new ArrayMap<>(count); + for (int i = 0; i < count; i++) { + String key = in.readString8(); + ArraySet value = (ArraySet) 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> 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 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> packageTagsMap) { + mPackageTags.ensureCapacity(packageTagsMap.size()); + for (Map.Entry> entry : packageTagsMap.entrySet()) { + Set newTags = entry.getValue(); + if (newTags.isEmpty()) { + add(entry.getKey()); + } else { + ArraySet 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> copy( + ArrayMap> value) { + int size = value.size(); + ArrayMap> copy = new ArrayMap<>(size); + for (int i = 0; i < size; i++) { + String packageName = value.keyAt(i); + ArraySet 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 3daa3a545a1dd..0c0d70c449a97 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -257,6 +257,14 @@ public final class DeviceConfig { @TestApi 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. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0c23ae6b4b29d..5cfb665745a8d 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 3cf46214fbec8..c112d09d40e4b 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 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 a9b47aab4d182..bd0de2984b7fd 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 mAllowIgnoreLocationSettings = new ArraySet<>(); + final ArrayMap> 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 getAllowIgnoreLocationSettings() { + public ArrayMap> 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 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 0000000000000..518e02e44b062 --- /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> 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 543504764ee38..0ec8f7d4f2ae2 100644 --- a/core/xsd/permission.xsd +++ b/core/xsd/permission.xsd @@ -98,6 +98,7 @@ + diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt index c36c422a852dc..f3beea1b96a94 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 6fa6536997c43..c9e4e0a9cb92b 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 0000000000000..92845745828b2 --- /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. + * + *

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. + * + *

Examples of valid entries: + * + *

    + *
  • android
  • + *
  • android;*
  • + *
  • android;*,com.example.app;null;my_attr
  • + *
  • android;*,com.example.app;null;my_attr,com.example.otherapp;my_attr
  • + *
+ */ + 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 7c2f5408fffb7..f83dc407d8701 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 String[] getIgnoreSettingsWhitelist() { + 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 2f54e21242477..e5eecb2068e06 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 3a824345c8a2b..f538875bf3372 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 c5582a0365493..541dcdc94a51b 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 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 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 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 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 perUserRestrictions; - SparseArray> perUserExcludedPackageTags; + SparseArray 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 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 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 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 634b6aa73539d..b9e97e89051bc 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 allowedPackages = null; + PackageTagsList allowedPackages = null; if (!enabled) { - ArrayMap> 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> 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 387ab77ebe3a4..148afa75ca751 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 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 632ed6ef192ad..c315da476d999 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 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 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 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 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 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>> mBaseValuesSupplier; + + @GuardedBy("this") + private boolean mValid; + @GuardedBy("this") + private PackageTagsList mCachedValue; + + PackageTagsListSetting(String name, + Supplier>> 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 a4a59564d0d1e..6d7f792507150 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 2ac50b63cfc62..b296ef2a1443b 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) null, oldUserId); + false, mOverlayToken, null, oldUserId); } // Apply the restrictions for the current user based on vr state - ArrayMap 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 2e101070f8b42..f1099f0e81842 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 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 getIgnoreSettingsPackageWhitelist() { - return mIgnoreSettingsPackageWhitelist.getValue(Set.class); + public PackageTagsList getIgnoreSettingsAllowlist() { + return mIgnoreSettingsAllowlist.getValue(PackageTagsList.class); } - public void setIgnoreSettingsPackageWhitelist(Set 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 00246dd21ed99..6bc3b60410707 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)