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

Commit e803a1bd authored by Winson's avatar Winson
Browse files

Add internal domain verification data classes

Eventually will be used to store state that's included as part of
com.android.server.pm.Settings.

Also adds equality and  Kotlin index operator mutation support to
SparseArray, to improve ease of use.

Exempt-From-Owner-Approval: Already approved by owners on main branch

Bug: 163565712
CTS-Coverage-Bug: 179382047

Test: none, will be tested as part of follow up change

Change-Id: Ie4eca3a99633465337758ee165e07f35c8db87c8
parent 2641e601
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -45795,6 +45795,8 @@ package android.util {
    method public void clear();
    method public android.util.SparseArray<E> clone();
    method public boolean contains(int);
    method public boolean contentEquals(@Nullable android.util.SparseArray<E>);
    method public int contentHashCode();
    method public void delete(int);
    method public E get(int);
    method public E get(int, E);
+43 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.util;

import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;

import com.android.internal.util.ArrayUtils;
@@ -23,6 +24,8 @@ import com.android.internal.util.GrowingArrayUtils;

import libcore.util.EmptyArray;

import java.util.Objects;

/**
 * <code>SparseArray</code> maps integers to Objects and, unlike a normal array of Objects,
 * its indices can contain gaps. <code>SparseArray</code> is intended to be more memory-efficient
@@ -505,4 +508,44 @@ public class SparseArray<E> implements Cloneable {
        buffer.append('}');
        return buffer.toString();
    }

    /**
     * For backwards compatibility reasons, {@link Object#equals(Object)} cannot be implemented,
     * so this serves as a manually invoked alternative.
     */
    public boolean contentEquals(@Nullable SparseArray<E> other) {
        if (other == null) {
            return false;
        }

        int size = size();
        if (size != other.size()) {
            return false;
        }

        for (int index = 0; index < size; index++) {
            int key = keyAt(index);
            if (!Objects.equals(valueAt(index), other.get(key))) {
                return false;
            }
        }

        return true;
    }

    /**
     * For backwards compatibility, {@link Object#hashCode()} cannot be implemented, so this serves
     * as a manually invoked alternative.
     */
    public int contentHashCode() {
        int hash = 0;
        int size = size();
        for (int index = 0; index < size; index++) {
            int key = keyAt(index);
            E value = valueAt(index);
            hash = 31 * hash + Objects.hashCode(key);
            hash = 31 * hash + Objects.hashCode(value);
        }
        return hash;
    }
}
+251 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.pm.domain.verify.models;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.domain.verify.DomainVerificationManager;
import android.content.pm.domain.verify.DomainVerificationState;
import android.util.ArrayMap;
import android.util.SparseArray;

import com.android.internal.util.DataClass;

import java.util.Objects;
import java.util.UUID;

/**
 * State for a single package for the domain verification APIs. Stores the state of each individual
 * domain declared by the package, including its verification state and user selection state.
 */
@DataClass(genToString = true, genEqualsHashCode = true)
public class DomainVerificationPkgState {

    @NonNull
    private final String mPackageName;

    @NonNull
    private UUID mId;

    /**
     * Whether or not the package declares any autoVerify domains. This is separate from an empty
     * check on the map itself, because an empty map means no response recorded, not necessarily no
     * domains declared. When this is false, {@link #mStateMap} will be empty, but
     * {@link #mUserSelectionStates} may contain any domains the user has explicitly chosen to
     * allow this package to open, which may or may not be marked autoVerify.
     */
    private final boolean mHasAutoVerifyDomains;

    /**
     * Map of domains to state integers. Only domains that are not set to the default value of
     * {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
     *
     * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
     *  such as storing no state when the package is marked as a linked app in SystemConfig.
     */
    @NonNull
    private final ArrayMap<String, Integer> mStateMap;

    @NonNull
    private final SparseArray<DomainVerificationUserState> mUserSelectionStates;

    public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id,
            boolean hasAutoVerifyDomains) {
        this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
    }

    @Nullable
    public DomainVerificationUserState getUserSelectionState(@UserIdInt int userId) {
        return mUserSelectionStates.get(userId);
    }

    @Nullable
    public DomainVerificationUserState getOrCreateUserSelectionState(@UserIdInt int userId) {
        DomainVerificationUserState userState = mUserSelectionStates.get(userId);
        if (userState == null) {
            userState = new DomainVerificationUserState(userId);
            mUserSelectionStates.put(userId, userState);
        }
        return userState;
    }

    public void setId(@NonNull UUID id) {
        mId = id;
    }

    public void removeUser(@UserIdInt int userId) {
        mUserSelectionStates.remove(userId);
    }

    public void removeAllUsers() {
        mUserSelectionStates.clear();
    }

    private int userSelectionStatesHashCode() {
        return mUserSelectionStates.contentHashCode();
    }

    private boolean userSelectionStatesEquals(
            @NonNull SparseArray<DomainVerificationUserState> other) {
        return mUserSelectionStates.contentEquals(other);
    }



    // Code below generated by codegen v1.0.22.
    //
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    //
    // To regenerate run:
    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java
    //
    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
    //   Settings > Editor > Code Style > Formatter Control
    //@formatter:off


    /**
     * Creates a new DomainVerificationPkgState.
     *
     * @param stateMap
     *   Map of domains to state integers. Only domains that are not set to the default value of
     *   {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
     *
     *   TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
     *    such as storing no state when the package is marked as a linked app in SystemConfig.
     */
    @DataClass.Generated.Member
    public DomainVerificationPkgState(
            @NonNull String packageName,
            @NonNull UUID id,
            boolean hasAutoVerifyDomains,
            @NonNull ArrayMap<String,Integer> stateMap,
            @NonNull SparseArray<DomainVerificationUserState> userSelectionStates) {
        this.mPackageName = packageName;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mPackageName);
        this.mId = id;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mId);
        this.mHasAutoVerifyDomains = hasAutoVerifyDomains;
        this.mStateMap = stateMap;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mStateMap);
        this.mUserSelectionStates = userSelectionStates;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mUserSelectionStates);

        // onConstructed(); // You can define this method to get a callback
    }

    @DataClass.Generated.Member
    public @NonNull String getPackageName() {
        return mPackageName;
    }

    @DataClass.Generated.Member
    public @NonNull UUID getId() {
        return mId;
    }

    @DataClass.Generated.Member
    public boolean isHasAutoVerifyDomains() {
        return mHasAutoVerifyDomains;
    }

    /**
     * Map of domains to state integers. Only domains that are not set to the default value of
     * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
     *
     * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
     *  such as storing no state when the package is marked as a linked app in SystemConfig.
     */
    @DataClass.Generated.Member
    public @NonNull ArrayMap<String,Integer> getStateMap() {
        return mStateMap;
    }

    @DataClass.Generated.Member
    public @NonNull SparseArray<DomainVerificationUserState> getUserSelectionStates() {
        return mUserSelectionStates;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
        // You can override field toString logic by defining methods like:
        // String fieldNameToString() { ... }

        return "DomainVerificationPkgState { " +
                "packageName = " + mPackageName + ", " +
                "id = " + mId + ", " +
                "hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " +
                "stateMap = " + mStateMap + ", " +
                "userSelectionStates = " + mUserSelectionStates +
        " }";
    }

    @Override
    @DataClass.Generated.Member
    public boolean equals(@Nullable Object o) {
        // You can override field equality logic by defining either of the methods like:
        // boolean fieldNameEquals(DomainVerificationPkgState other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }

        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        DomainVerificationPkgState that = (DomainVerificationPkgState) o;
        //noinspection PointlessBooleanExpression
        return true
                && Objects.equals(mPackageName, that.mPackageName)
                && Objects.equals(mId, that.mId)
                && mHasAutoVerifyDomains == that.mHasAutoVerifyDomains
                && Objects.equals(mStateMap, that.mStateMap)
                && userSelectionStatesEquals(that.mUserSelectionStates);
    }

    @Override
    @DataClass.Generated.Member
    public int hashCode() {
        // You can override field hashCode logic by defining methods like:
        // int fieldNameHashCode() { ... }

        int _hash = 1;
        _hash = 31 * _hash + Objects.hashCode(mPackageName);
        _hash = 31 * _hash + Objects.hashCode(mId);
        _hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains);
        _hash = 31 * _hash + Objects.hashCode(mStateMap);
        _hash = 31 * _hash + userSelectionStatesHashCode();
        return _hash;
    }

    @DataClass.Generated(
            time = 1608234185474L,
            codegenVersion = "1.0.22",
            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java",
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final  boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.domain.verify.models.DomainVerificationUserState> mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.domain.verify.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.domain.verify.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic  void setId(java.util.UUID)\npublic  void removeUser(int)\npublic  void removeAllUsers()\nprivate  int userSelectionStatesHashCode()\nprivate  boolean userSelectionStatesEquals(android.util.SparseArray<com.android.server.pm.domain.verify.models.DomainVerificationUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
    @Deprecated
    private void __metadata() {}


    //@formatter:on
    // End of generated code

}
+122 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.pm.domain.verify.models;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

/**
 * A feature specific implementation of a multi-key map, since lookups by both a {@link String}
 * package name and {@link UUID} domain set ID should be supported.
 *
 * @param <ValueType> stored object type
 */
public class DomainVerificationStateMap<ValueType> {

    private static final String TAG = "DomainVerificationStateMap";

    @NonNull
    private final ArrayMap<String, ValueType> mPackageNameMap = new ArrayMap<>();

    @NonNull
    private final ArrayMap<UUID, ValueType> mDomainSetIdMap = new ArrayMap<>();

    public int size() {
        return mPackageNameMap.size();
    }

    @NonNull
    public ValueType valueAt(@IntRange(from = 0) int index) {
        return mPackageNameMap.valueAt(index);
    }

    @Nullable
    public ValueType get(@NonNull String packageName) {
        return mPackageNameMap.get(packageName);
    }

    @Nullable
    public ValueType get(@NonNull UUID domainSetId) {
        return mDomainSetIdMap.get(domainSetId);
    }

    public void put(@NonNull String packageName, @NonNull UUID domainSetId,
            @NonNull ValueType valueType) {
        if (mPackageNameMap.containsKey(packageName)) {
            remove(packageName);
        }

        mPackageNameMap.put(packageName, valueType);
        mDomainSetIdMap.put(domainSetId, valueType);
    }

    @Nullable
    public ValueType remove(@NonNull String packageName) {
        ValueType valueRemoved = mPackageNameMap.remove(packageName);
        if (valueRemoved != null) {
            int index = mDomainSetIdMap.indexOfValue(valueRemoved);
            if (index >= 0) {
                mDomainSetIdMap.removeAt(index);
            }
        }
        return valueRemoved;
    }

    @Nullable
    public ValueType remove(@NonNull UUID domainSetId) {
        ValueType valueRemoved = mDomainSetIdMap.remove(domainSetId);
        if (valueRemoved != null) {
            int index = mPackageNameMap.indexOfValue(valueRemoved);
            if (index >= 0) {
                mPackageNameMap.removeAt(index);
            }
        }
        return valueRemoved;
    }

    @NonNull
    public List<String> getPackageNames() {
        return new ArrayList<>(mPackageNameMap.keySet());
    }

    /**
     * Exposes the backing values collection of the one of the internal maps. Should only be used
     * for test assertions.
     */
    @VisibleForTesting
    public Collection<ValueType> values() {
        return new ArrayList<>(mPackageNameMap.values());
    }

    @Override
    public String toString() {
        return "DomainVerificationStateMap{"
                + "packageNameMap=" + mPackageNameMap
                + ", domainSetIdMap=" + mDomainSetIdMap
                + '}';
    }
}
+185 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.pm.domain.verify.models;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.util.ArraySet;

import com.android.internal.util.DataClass;

import java.util.Set;

/**
 * Tracks which domains have been explicitly enabled by the user, allowing it to automatically open
 * that domain when a web URL Intent is sent ft.
 */
@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true)
public class DomainVerificationUserState {

    @UserIdInt
    private final int mUserId;

    /** List of domains which have been enabled by the user. **/
    @NonNull
    private final ArraySet<String> mEnabledHosts;

    /** Whether to disallow this package from automatically opening links by auto verification. */
    private boolean mDisallowLinkHandling;

    public DomainVerificationUserState(@UserIdInt int userId) {
        mUserId = userId;
        mEnabledHosts = new ArraySet<>();
    }

    public DomainVerificationUserState addHosts(@NonNull ArraySet<String> newHosts) {
        mEnabledHosts.addAll(newHosts);
        return this;
    }

    public DomainVerificationUserState addHosts(@NonNull Set<String> newHosts) {
        mEnabledHosts.addAll(newHosts);
        return this;
    }

    public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) {
        mEnabledHosts.removeAll(newHosts);
        return this;
    }

    public DomainVerificationUserState removeHosts(@NonNull Set<String> newHosts) {
        mEnabledHosts.removeAll(newHosts);
        return this;
    }


    // Code below generated by codegen v1.0.22.
    //
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    //
    // To regenerate run:
    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java
    //
    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
    //   Settings > Editor > Code Style > Formatter Control
    //@formatter:off


    /**
     * Creates a new DomainVerificationUserState.
     *
     * @param enabledHosts
     *   List of domains which have been enabled by the user. *
     */
    @DataClass.Generated.Member
    public DomainVerificationUserState(
            @UserIdInt int userId,
            @NonNull ArraySet<String> enabledHosts,
            boolean disallowLinkHandling) {
        this.mUserId = userId;
        com.android.internal.util.AnnotationValidations.validate(
                UserIdInt.class, null, mUserId);
        this.mEnabledHosts = enabledHosts;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mEnabledHosts);
        this.mDisallowLinkHandling = disallowLinkHandling;

        // onConstructed(); // You can define this method to get a callback
    }

    @DataClass.Generated.Member
    public @UserIdInt int getUserId() {
        return mUserId;
    }

    /**
     * List of domains which have been enabled by the user. *
     */
    @DataClass.Generated.Member
    public @NonNull ArraySet<String> getEnabledHosts() {
        return mEnabledHosts;
    }

    @DataClass.Generated.Member
    public boolean isDisallowLinkHandling() {
        return mDisallowLinkHandling;
    }

    @DataClass.Generated.Member
    public @NonNull DomainVerificationUserState setDisallowLinkHandling( boolean value) {
        mDisallowLinkHandling = value;
        return this;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
        // You can override field toString logic by defining methods like:
        // String fieldNameToString() { ... }

        return "DomainVerificationUserState { " +
                "userId = " + mUserId + ", " +
                "enabledHosts = " + mEnabledHosts + ", " +
                "disallowLinkHandling = " + mDisallowLinkHandling +
        " }";
    }

    @Override
    @DataClass.Generated.Member
    public boolean equals(@android.annotation.Nullable Object o) {
        // You can override field equality logic by defining either of the methods like:
        // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }

        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        DomainVerificationUserState that = (DomainVerificationUserState) o;
        //noinspection PointlessBooleanExpression
        return true
                && mUserId == that.mUserId
                && java.util.Objects.equals(mEnabledHosts, that.mEnabledHosts)
                && mDisallowLinkHandling == that.mDisallowLinkHandling;
    }

    @Override
    @DataClass.Generated.Member
    public int hashCode() {
        // You can override field hashCode logic by defining methods like:
        // int fieldNameHashCode() { ... }

        int _hash = 1;
        _hash = 31 * _hash + mUserId;
        _hash = 31 * _hash + java.util.Objects.hashCode(mEnabledHosts);
        _hash = 31 * _hash + Boolean.hashCode(mDisallowLinkHandling);
        return _hash;
    }

    @DataClass.Generated(
            time = 1608234273324L,
            codegenVersion = "1.0.22",
            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java",
            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate  boolean mDisallowLinkHandling\npublic  com.android.server.pm.domain.verify.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.domain.verify.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.domain.verify.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.domain.verify.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)")
    @Deprecated
    private void __metadata() {}


    //@formatter:on
    // End of generated code

}