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

Commit ce7336d0 authored by Grace Cheng's avatar Grace Cheng Committed by Android (Google) Code Review
Browse files

Merge "Adds PackageTagsMap." into main

parents 25591093 c84967b8
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -430,6 +430,28 @@ package android.os {
    method public boolean shouldBypassCache(@NonNull Q);
  }

  @FlaggedApi("android.location.flags.change_get_adas_allowlist_from_hidden_to_system") public final class PackageTagsMap implements android.os.Parcelable {
    method public boolean contains(@NonNull String, @Nullable String);
    method public boolean containsAll(@NonNull android.os.PackageTagsMap);
    method public boolean containsPackage(@NonNull String);
    method public boolean containsPackageWithAllTags(@NonNull String);
    method public int describeContents();
    method @NonNull public java.util.Set<java.lang.String> getPackages();
    method public boolean isEmpty();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.PackageTagsMap> CREATOR;
  }

  public static final class PackageTagsMap.Builder {
    ctor public PackageTagsMap.Builder();
    ctor public PackageTagsMap.Builder(int);
    method @NonNull public android.os.PackageTagsMap.Builder add(@NonNull String);
    method @NonNull public android.os.PackageTagsMap.Builder add(@NonNull String, @Nullable String);
    method @NonNull public android.os.PackageTagsMap.Builder addAll(@NonNull android.os.PackageTagsMap);
    method @NonNull public android.os.PackageTagsMap.Builder addAll(@NonNull java.util.Map<java.lang.String,? extends java.util.Set<java.lang.String>>);
    method @NonNull public android.os.PackageTagsMap build();
  }

  public class Process {
    method public static final int toSdkSandboxUid(int);
    field public static final int NFC_UID = 1027; // 0x403
+22 −0
Original line number Diff line number Diff line
@@ -2549,6 +2549,28 @@ package android.os {
    method @NonNull public android.os.PackageTagsList.Builder clear();
  }

  @FlaggedApi("android.location.flags.change_get_adas_allowlist_from_hidden_to_system") public final class PackageTagsMap implements android.os.Parcelable {
    method public boolean contains(@NonNull String, @Nullable String);
    method public boolean containsAll(@NonNull android.os.PackageTagsMap);
    method public boolean containsPackage(@NonNull String);
    method public boolean containsPackageWithAllTags(@NonNull String);
    method public int describeContents();
    method @NonNull public java.util.Set<java.lang.String> getPackages();
    method public boolean isEmpty();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.PackageTagsMap> CREATOR;
  }

  public static final class PackageTagsMap.Builder {
    ctor public PackageTagsMap.Builder();
    ctor public PackageTagsMap.Builder(int);
    method @NonNull public android.os.PackageTagsMap.Builder add(@NonNull String);
    method @NonNull public android.os.PackageTagsMap.Builder add(@NonNull String, @Nullable String);
    method @NonNull public android.os.PackageTagsMap.Builder addAll(@NonNull android.os.PackageTagsMap);
    method @NonNull public android.os.PackageTagsMap.Builder addAll(@NonNull java.util.Map<java.lang.String,? extends java.util.Set<java.lang.String>>);
    method @NonNull public android.os.PackageTagsMap build();
  }

  public final class Parcel {
    method public boolean allowSquashing();
    method public int getFlags();
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 PackageTagsMap;
+435 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.location.flags.Flags;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.annotations.Immutable;

import java.io.PrintWriter;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * A list of packages and associated attribution tags that supports easy membership checks. Supports
 * "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to
 * standard checks.
 *
 * @hide
 */
@FlaggedApi(Flags.FLAG_CHANGE_GET_ADAS_ALLOWLIST_FROM_HIDDEN_TO_SYSTEM)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi
@Immutable
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PackageTagsMap implements Parcelable {

    // an empty set value matches any attribution tag (ie, wildcard)
    private final ArrayMap<String, ArraySet<String>> mPackageTags;

    private PackageTagsMap(@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 found within this instance. If this returns true this
     * does not imply anything about whether any given attribution tag under the given package name
     * is present.
     */
    public boolean containsPackage(@NonNull String packageName) {
        return mPackageTags.containsKey(packageName);
    }

    /**
     * Returns true if the given attribution tag is found within this instance under any package.
     * Only returns true if the attribution tag literal is found, not if any package contains the
     * set of all attribution tags.
     *
     * @hide
     */
    public boolean containsTag(@NonNull String attributionTag) {
        final int size = mPackageTags.size();
        for (int i = 0; i < size; i++) {
            ArraySet<String> tags = mPackageTags.valueAt(i);
            if (tags.contains(attributionTag)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns true if all attribution tags under the given package are contained within this
     * instance.
     */
    public boolean containsPackageWithAllTags(@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()) {
            // our tags are the full set, so we contain any attribution tag
            return true;
        } else {
            return tags.contains(attributionTag);
        }
    }

    /** Returns true if the given PackageTagsMap is a subset of this instance. */
    public boolean containsAll(@NonNull PackageTagsMap packageTagsMap) {
        int otherSize = packageTagsMap.mPackageTags.size();
        if (otherSize > mPackageTags.size()) {
            return false;
        }

        for (int i = 0; i < otherSize; i++) {
            String packageName = packageTagsMap.mPackageTags.keyAt(i);
            ArraySet<String> tags = mPackageTags.get(packageName);
            if (tags == null) {
                return false;
            }
            if (tags.isEmpty()) {
                // our tags are the full set, so we contain whatever the other tags are
                continue;
            }
            ArraySet<String> otherTags = packageTagsMap.mPackageTags.valueAt(i);
            if (otherTags.isEmpty()) {
                // other tags are the full set, so we can't contain them
                return false;
            }
            if (!tags.containsAll(otherTags)) {
                return false;
            }
        }

        return true;
    }

    /** Returns all packages that possess at least one attribution tag. */
    public @NonNull Set<String> getPackages() {
        return mPackageTags.keySet().stream().collect(Collectors.toSet());
    }

    public static final @NonNull Parcelable.Creator<PackageTagsMap> CREATOR =
            new Parcelable.Creator<PackageTagsMap>() {
                @SuppressWarnings("unchecked")
                @Override
                public PackageTagsMap 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 PackageTagsMap(packageTags);
                }

                @Override
                public PackageTagsMap[] newArray(int size) {
                    return new PackageTagsMap[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 PackageTagsMap)) {
            return false;
        }

        PackageTagsMap that = (PackageTagsMap) 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 != null && attributionTag.startsWith(packageName)) {
                        pw.print(attributionTag.substring(packageName.length()));
                    } else {
                        pw.print(attributionTag);
                    }
                }
            }
            pw.println("]");
        }
    }

    /** Builder class for {@link PackageTagsMap}. */
    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 package and set of attribution tags to the builder.
         *
         * @hide
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder add(
                @NonNull String packageName, @NonNull Collection<String> attributionTags) {
            if (attributionTags.isEmpty()) {
                // the input is not allowed to specify a full set by passing in an empty collection
                return this;
            }

            ArraySet<String> tags = mPackageTags.get(packageName);
            if (tags == null) {
                tags = new ArraySet<>(attributionTags);
                mPackageTags.put(packageName, tags);
            } else if (!tags.isEmpty()) {
                // if we contain the full set, already done, otherwise add all the tags
                tags.addAll(attributionTags);
            }

            return this;
        }

        /** Adds the specified {@link PackageTagsMap} to the builder. */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder addAll(@NonNull PackageTagsMap packageTagsMap) {
            return addAll(packageTagsMap.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 addAll(@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 {
                    add(entry.getKey(), newTags);
                }
            }

            return this;
        }

        /**
         * Removes all attribution tags under the specified package from the builder.
         *
         * @hide
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder remove(@NonNull String packageName) {
            mPackageTags.remove(packageName);
            return this;
        }

        /**
         * Removes the specified package and attribution tag from the builder if and only if the
         * specified attribution tag is listed explicitly under the package. If the package contains
         * all possible attribution tags, then nothing will be removed.
         *
         * @hide
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder remove(
                @NonNull String packageName, @Nullable String attributionTag) {
            ArraySet<String> tags = mPackageTags.get(packageName);
            if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) {
                mPackageTags.remove(packageName);
            }
            return this;
        }

        /**
         * Removes the specified package and set of attribution tags from the builder if and only if
         * the specified set of attribution tags are listed explicitly under the package. If the
         * package contains all possible attribution tags, then nothing will be removed.
         *
         * @hide
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder remove(
                @NonNull String packageName, @NonNull Collection<String> attributionTags) {
            if (attributionTags.isEmpty()) {
                // the input is not allowed to specify a full set by passing in an empty collection
                return this;
            }

            ArraySet<String> tags = mPackageTags.get(packageName);
            if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) {
                mPackageTags.remove(packageName);
            }
            return this;
        }

        /**
         * Removes the specified {@link PackageTagsMap} from the builder. If a package contains all
         * possible attribution tags, it will only be removed if the package in the removed {@link
         * PackageTagsMap} also contains all possible attribution tags.
         *
         * @hide
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder removeAll(@NonNull PackageTagsMap packageTagsMap) {
            return removeAll(packageTagsMap.mPackageTags);
        }

        /**
         * Removes the given map of package to attribution tags to the builder. An empty set of
         * attribution tags is interpreted to imply all attribution tags under that package. If a
         * package contains all possible attribution tags, it will only be removed if the package in
         * the removed map also contains all possible attribution tags.
         *
         * @hide
         */
        @SuppressLint("MissingGetterMatchingBuilder")
        public @NonNull Builder removeAll(
                @NonNull Map<String, ? extends Set<String>> packageTagsMap) {
            for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
                Set<String> removedTags = entry.getValue();
                if (removedTags.isEmpty()) {
                    // if removing the full set, drop the package completely
                    remove(entry.getKey());
                } else {
                    remove(entry.getKey(), removedTags);
                }
            }

            return this;
        }

        /**
         * Clears the builder.
         *
         * @hide
         */
        public @NonNull Builder clear() {
            mPackageTags.clear();
            return this;
        }

        /** Constructs a new {@link PackageTagsMap}. */
        public @NonNull PackageTagsMap build() {
            return new PackageTagsMap(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;
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -276,6 +276,7 @@ android.os.Looper
android.os.Message
android.os.MessageQueue
android.os.PackageTagsList
android.os.PackageTagsMap
android.os.Parcel
android.os.ParcelFileDescriptor
android.os.ParcelFormatException