Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ea81834 authored by Soonil Nagarkar's avatar Soonil Nagarkar
Browse files

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
parent f0ec6471
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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";
+29 −2
Original line number Diff line number Diff line
@@ -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";
+4 −3
Original line number Diff line number Diff line
@@ -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) {
+18 −0
Original line number Diff line number Diff line
/* 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;
+310 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
Loading