From 2641e601cc0bebc09336a83974f121b4bfc08ae2 Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 15 Dec 2020 10:26:28 -0800 Subject: [PATCH] Add domain verification API classes Includes the request class sent to the verification agent and the 2 data classes returned by the to-be-added DomainVerificationManager. Exempt-From-Owner-Approval: Already approved by owners on main branch Bug: 163565712 CTS-Coverage-Bug: 179382047 Test: atest DomainVerificationCoreApiTest Change-Id: If9f1b987f0d06c930f6c44b58af101b83947acb9 --- core/api/system-current.txt | 31 ++ .../verify/DomainVerificationRequest.java | 190 ++++++++++ .../domain/verify/DomainVerificationSet.aidl | 19 + .../domain/verify/DomainVerificationSet.java | 336 +++++++++++++++++ .../DomainVerificationUserSelection.aidl | 19 + .../DomainVerificationUserSelection.java | 342 ++++++++++++++++++ .../content/pm/domain/verify/TEST_MAPPING | 12 + .../com/android/internal/util/Parcelling.java | 15 + .../unit/Android.bp | 27 ++ .../unit/AndroidManifest.xml | 27 ++ .../unit/AndroidTest.xml | 30 ++ .../verify/DomainVerificationCoreApiTest.kt | 174 +++++++++ .../DomainVerificationModelExtensions.kt | 33 ++ 13 files changed, 1255 insertions(+) create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationRequest.java create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationSet.java create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl create mode 100644 core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java create mode 100644 core/java/android/content/pm/domain/verify/TEST_MAPPING create mode 100644 services/tests/PackageManagerServiceTests/unit/Android.bp create mode 100644 services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml create mode 100644 services/tests/PackageManagerServiceTests/unit/AndroidTest.xml create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt create mode 100644 services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 3a7571c8e48b7..9a95dacef7ecd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2696,6 +2696,37 @@ package android.content.pm.dex { } +package android.content.pm.domain.verify { + + public final class DomainVerificationRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Set getPackageNames(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public final class DomainVerificationSet implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map getHostToStateMap(); + method @NonNull public java.util.UUID getIdentifier(); + method @NonNull public String getPackageName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public final class DomainVerificationUserSelection implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map getHostToUserSelectionMap(); + method @NonNull public java.util.UUID getIdentifier(); + method @NonNull public String getPackageName(); + method @NonNull public android.os.UserHandle getUser(); + method @NonNull public boolean isLinkHandlingAllowed(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + +} + package android.content.pm.permission { @Deprecated public final class RuntimePermissionPresentationInfo implements android.os.Parcelable { diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java b/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java new file mode 100644 index 0000000000000..46930ab528205 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java @@ -0,0 +1,190 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.Intent; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + +import java.util.Set; + +/** + * Request object sent in the {@link Intent} that's broadcast to the domain verification + * agent, retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}. + *

+ * This contains the set of packages which have been invalidated and will require + * re-verification. The exact domains can be retrieved with + * {@link DomainVerificationManager#getDomainVerificationSet(String)} + * + * @hide + */ +@SuppressWarnings("DefaultAnnotationParam") +@DataClass(genHiddenConstructor = true, genAidl = false, genEqualsHashCode = true) +@SystemApi +public final class DomainVerificationRequest implements Parcelable { + + /** + * The package names of the apps that need to be verified. The receiver should call + * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * these values to get the actual set of domains that need to be acted on. + */ + @NonNull + @DataClass.ParcelWith(Parcelling.BuiltIn.ForStringSet.class) + private final Set mPackageNames; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationRequest. + * + * @param packageNames + * The package names of the apps that need to be verified. The receiver should call + * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * these values to get the actual set of domains that need to be acted on. + * @hide + */ + @DataClass.Generated.Member + public DomainVerificationRequest( + @NonNull Set packageNames) { + this.mPackageNames = packageNames; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageNames); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The package names of the apps that need to be verified. The receiver should call + * {@link DomainVerificationManager#getDomainVerificationSet(String)} with each of + * these values to get the actual set of domains that need to be acted on. + */ + @DataClass.Generated.Member + public @NonNull Set getPackageNames() { + return mPackageNames; + } + + @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(DomainVerificationRequest other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationRequest that = (DomainVerificationRequest) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mPackageNames, that.mPackageNames); + } + + @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 + java.util.Objects.hashCode(mPackageNames); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling> sParcellingForPackageNames = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForStringSet.class); + static { + if (sParcellingForPackageNames == null) { + sParcellingForPackageNames = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForStringSet()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + sParcellingForPackageNames.parcel(mPackageNames, dest, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + Set packageNames = sParcellingForPackageNames.unparcel(in); + + this.mPackageNames = packageNames; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageNames); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public DomainVerificationRequest[] newArray(int size) { + return new DomainVerificationRequest[size]; + } + + @Override + public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationRequest(in); + } + }; + + @DataClass.Generated( + time = 1611795646938L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationRequest.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl b/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl new file mode 100644 index 0000000000000..0208907224e6c --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationSet.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.content.pm.domain.verify; + +parcelable DomainVerificationSet; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationSet.java b/core/java/android/content/pm/domain/verify/DomainVerificationSet.java new file mode 100644 index 0000000000000..bc076505bae23 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationSet.java @@ -0,0 +1,336 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.pm.PackageManager; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Contains the state of all domains for a given package on device. Used by the domain verification + * agent to determine the domains declared by a package that need to be verified by comparing + * against the digital asset links response from the server hosting that domain. + *

+ * These values for each domain can be modified through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + * + * @hide + */ +@SystemApi +@SuppressWarnings("DefaultAnnotationParam") +@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, + genEqualsHashCode = true) +public final class DomainVerificationSet implements Parcelable { + + /** + * A domain verification ID for use in later API calls. This represents the snapshot + * of the domains for a package on device, and will be invalidated whenever the + * package changes. + *

+ * An exception will be thrown at the next API call that receives the ID if it is no + * longer valid. + *

+ * The caller may also be notified with a broadcast whenever a package and ID is + * invalidated, at which point it can use the package name to evict existing + * requests with an invalid set ID. If the caller wants to manually check if any + * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} + * API will allow tracking the packages changed since the last query of this + * method, prompting the caller to re-query. + *

+ * This allows the caller to arbitrarily grant or revoke domain verification + * status, through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + */ + @NonNull + @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class) + private final UUID mIdentifier; + + /** + * The package name that this data corresponds to. + */ + @NonNull + private final String mPackageName; + + /** + * Map of host names to their current state. State is an integer, which defaults to + * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the + * domain verification agent (the intended consumer of this API), which can be equal + * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or + * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for + * any unsuccessful response. + *

+ * Any value non-inclusive between those 2 values are reserved for use by the system. + * The domain verification agent may be able to act on these reserved values, and this + * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. + * It is expected that the agent attempt to verify all domains that it can modify the + * state of, even if it does not understand the meaning of those values. + */ + @NonNull + private final Map mHostToStateMap; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationSet.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationSet. + * + * @param identifier + * A domain verification ID for use in later API calls. This represents the snapshot + * of the domains for a package on device, and will be invalidated whenever the + * package changes. + *

+ * An exception will be thrown at the next API call that receives the ID if it is no + * longer valid. + *

+ * The caller may also be notified with a broadcast whenever a package and ID is + * invalidated, at which point it can use the package name to evict existing + * requests with an invalid set ID. If the caller wants to manually check if any + * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} + * API will allow tracking the packages changed since the last query of this + * method, prompting the caller to re-query. + *

+ * This allows the caller to arbitrarily grant or revoke domain verification + * status, through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + * @param packageName + * The package name that this data corresponds to. + * @param hostToStateMap + * Map of host names to their current state. State is an integer, which defaults to + * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the + * domain verification agent (the intended consumer of this API), which can be equal + * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or + * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for + * any unsuccessful response. + *

+ * Any value non-inclusive between those 2 values are reserved for use by the system. + * The domain verification agent may be able to act on these reserved values, and this + * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. + * It is expected that the agent attempt to verify all domains that it can modify the + * state of, even if it does not understand the meaning of those values. + * @hide + */ + @DataClass.Generated.Member + public DomainVerificationSet( + @NonNull UUID identifier, + @NonNull String packageName, + @NonNull Map hostToStateMap) { + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mHostToStateMap = hostToStateMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToStateMap); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * A domain verification ID for use in later API calls. This represents the snapshot + * of the domains for a package on device, and will be invalidated whenever the + * package changes. + *

+ * An exception will be thrown at the next API call that receives the ID if it is no + * longer valid. + *

+ * The caller may also be notified with a broadcast whenever a package and ID is + * invalidated, at which point it can use the package name to evict existing + * requests with an invalid set ID. If the caller wants to manually check if any + * IDs have been invalidate, the {@link PackageManager#getChangedPackages(int)} + * API will allow tracking the packages changed since the last query of this + * method, prompting the caller to re-query. + *

+ * This allows the caller to arbitrarily grant or revoke domain verification + * status, through + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + */ + @DataClass.Generated.Member + public @NonNull UUID getIdentifier() { + return mIdentifier; + } + + /** + * The package name that this data corresponds to. + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + /** + * Map of host names to their current state. State is an integer, which defaults to + * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the + * domain verification agent (the intended consumer of this API), which can be equal + * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or + * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for + * any unsuccessful response. + *

+ * Any value non-inclusive between those 2 values are reserved for use by the system. + * The domain verification agent may be able to act on these reserved values, and this + * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. + * It is expected that the agent attempt to verify all domains that it can modify the + * state of, even if it does not understand the meaning of those values. + */ + @DataClass.Generated.Member + public @NonNull Map getHostToStateMap() { + return mHostToStateMap; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainVerificationSet { " + + "identifier = " + mIdentifier + ", " + + "packageName = " + mPackageName + ", " + + "hostToStateMap = " + mHostToStateMap + + " }"; + } + + @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(DomainVerificationSet other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationSet that = (DomainVerificationSet) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mIdentifier, that.mIdentifier) + && java.util.Objects.equals(mPackageName, that.mPackageName) + && java.util.Objects.equals(mHostToStateMap, that.mHostToStateMap); + } + + @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 + java.util.Objects.hashCode(mIdentifier); + _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostToStateMap); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling sParcellingForIdentifier = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForUUID.class); + static { + if (sParcellingForIdentifier == null) { + sParcellingForIdentifier = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForUUID()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + sParcellingForIdentifier.parcel(mIdentifier, dest, flags); + dest.writeString(mPackageName); + dest.writeMap(mHostToStateMap); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainVerificationSet(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + UUID identifier = sParcellingForIdentifier.unparcel(in); + String packageName = in.readString(); + Map hostToStateMap = new java.util.LinkedHashMap<>(); + in.readMap(hostToStateMap, Integer.class.getClassLoader()); + + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mHostToStateMap = hostToStateMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToStateMap); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public DomainVerificationSet[] newArray(int size) { + return new DomainVerificationSet[size]; + } + + @Override + public DomainVerificationSet createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationSet(in); + } + }; + + @DataClass.Generated( + time = 1611795504275L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationSet.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map mHostToStateMap\nclass DomainVerificationSet extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl new file mode 100644 index 0000000000000..edcdb76813e7c --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.content.pm.domain.verify; + +parcelable DomainVerificationUserSelection; diff --git a/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java new file mode 100644 index 0000000000000..2e26ef3c97dad --- /dev/null +++ b/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java @@ -0,0 +1,342 @@ +/* + * 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 android.content.pm.domain.verify; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Parcelable; +import android.os.UserHandle; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Contains the user selection state for a package. This means all web HTTP(S) domains + * declared by a package in its manifest, whether or not they were marked for auto + * verification. + *

+ * By default, all apps are allowed to automatically open links with domains that they've + * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. + * The user can decide to disable this, disallowing the application from opening these + * links. + *

+ * Separately, independent of this toggle, the user can choose specific domains to allow + * an app to open, which is reflected as part of {@link #getHostToUserSelectionMap()}, + * which maps the domain name to the true/false state of whether it was enabled by the user. + *

+ * These values can be changed through the + * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, + * boolean)} and + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)} APIs. + *

+ * Note that because state is per user, if a different user needs to be changed, one will + * need to use {@link Context#createContextAsUser(UserHandle, int)} and hold the + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission. + * + * @hide + */ +@SystemApi +@SuppressWarnings("DefaultAnnotationParam") +@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, + genEqualsHashCode = true) +public final class DomainVerificationUserSelection implements Parcelable { + + /** + * @see DomainVerificationSet#getIdentifier + */ + @NonNull + @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class) + private final UUID mIdentifier; + + /** + * The package name that this data corresponds to. + */ + @NonNull + private final String mPackageName; + + /** + * The user that this data corresponds to. + */ + @NonNull + private final UserHandle mUser; + + /** + * Whether or not this package is allowed to open links. + */ + @NonNull + private final boolean mLinkHandlingAllowed; + + /** + * Retrieve the existing user selection state for the matching + * {@link #getPackageName()}, as was previously set by + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)}. + * + * @return Map of hosts to enabled state for the given package and user. + */ + @NonNull + private final Map mHostToUserSelectionMap; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainVerificationUserSelection. + * + * @param packageName + * The package name that this data corresponds to. + * @param user + * The user that this data corresponds to. + * @param linkHandlingAllowed + * Whether or not this package is allowed to open links. + * @param hostToUserSelectionMap + * Retrieve the existing user selection state for the matching + * {@link #getPackageName()}, as was previously set by + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)}. + * @hide + */ + @DataClass.Generated.Member + public DomainVerificationUserSelection( + @NonNull UUID identifier, + @NonNull String packageName, + @NonNull UserHandle user, + @NonNull boolean linkHandlingAllowed, + @NonNull Map hostToUserSelectionMap) { + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mUser = user; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mUser); + this.mLinkHandlingAllowed = linkHandlingAllowed; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLinkHandlingAllowed); + this.mHostToUserSelectionMap = hostToUserSelectionMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToUserSelectionMap); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * @see DomainVerificationSet#getIdentifier + */ + @DataClass.Generated.Member + public @NonNull UUID getIdentifier() { + return mIdentifier; + } + + /** + * The package name that this data corresponds to. + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + /** + * The user that this data corresponds to. + */ + @DataClass.Generated.Member + public @NonNull UserHandle getUser() { + return mUser; + } + + /** + * Whether or not this package is allowed to open links. + */ + @DataClass.Generated.Member + public @NonNull boolean isLinkHandlingAllowed() { + return mLinkHandlingAllowed; + } + + /** + * Retrieve the existing user selection state for the matching + * {@link #getPackageName()}, as was previously set by + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)}. + * + * @return Map of hosts to enabled state for the given package and user. + */ + @DataClass.Generated.Member + public @NonNull Map getHostToUserSelectionMap() { + return mHostToUserSelectionMap; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainVerificationUserSelection { " + + "identifier = " + mIdentifier + ", " + + "packageName = " + mPackageName + ", " + + "user = " + mUser + ", " + + "linkHandlingAllowed = " + mLinkHandlingAllowed + ", " + + "hostToUserSelectionMap = " + mHostToUserSelectionMap + + " }"; + } + + @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(DomainVerificationUserSelection other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainVerificationUserSelection that = (DomainVerificationUserSelection) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mIdentifier, that.mIdentifier) + && java.util.Objects.equals(mPackageName, that.mPackageName) + && java.util.Objects.equals(mUser, that.mUser) + && mLinkHandlingAllowed == that.mLinkHandlingAllowed + && java.util.Objects.equals(mHostToUserSelectionMap, that.mHostToUserSelectionMap); + } + + @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 + java.util.Objects.hashCode(mIdentifier); + _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName); + _hash = 31 * _hash + java.util.Objects.hashCode(mUser); + _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostToUserSelectionMap); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling sParcellingForIdentifier = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForUUID.class); + static { + if (sParcellingForIdentifier == null) { + sParcellingForIdentifier = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForUUID()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mLinkHandlingAllowed) flg |= 0x8; + dest.writeByte(flg); + sParcellingForIdentifier.parcel(mIdentifier, dest, flags); + dest.writeString(mPackageName); + dest.writeTypedObject(mUser, flags); + dest.writeMap(mHostToUserSelectionMap); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + boolean linkHandlingAllowed = (flg & 0x8) != 0; + UUID identifier = sParcellingForIdentifier.unparcel(in); + String packageName = in.readString(); + UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR); + Map hostToUserSelectionMap = new java.util.LinkedHashMap<>(); + in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader()); + + this.mIdentifier = identifier; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mIdentifier); + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mUser = user; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mUser); + this.mLinkHandlingAllowed = linkHandlingAllowed; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLinkHandlingAllowed); + this.mHostToUserSelectionMap = hostToUserSelectionMap; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHostToUserSelectionMap); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public DomainVerificationUserSelection[] newArray(int size) { + return new DomainVerificationUserSelection[size]; + } + + @Override + public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) { + return new DomainVerificationUserSelection(in); + } + }; + + @DataClass.Generated( + time = 1611799495498L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java", + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/domain/verify/TEST_MAPPING b/core/java/android/content/pm/domain/verify/TEST_MAPPING new file mode 100644 index 0000000000000..ffb1d9a600846 --- /dev/null +++ b/core/java/android/content/pm/domain/verify/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "PackageManagerServiceUnitTests", + "options": [ + { + "include-filter": "com.android.server.pm.test.domain.verify" + } + ] + } + ] +} diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java index dd64c402fdbf4..1ab316d07e42d 100644 --- a/core/java/com/android/internal/util/Parcelling.java +++ b/core/java/com/android/internal/util/Parcelling.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.regex.Pattern; /** @@ -291,5 +292,19 @@ public interface Parcelling { return s == null ? null : Pattern.compile(s); } } + + class ForUUID implements Parcelling { + + @Override + public void parcel(UUID item, Parcel dest, int parcelFlags) { + dest.writeString(item == null ? null : item.toString()); + } + + @Override + public UUID unparcel(Parcel source) { + String string = source.readString(); + return string == null ? null : UUID.fromString(string); + } + } } } diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp new file mode 100644 index 0000000000000..fed5f45e6ffd3 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -0,0 +1,27 @@ +// 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. + +android_test { + name: "PackageManagerServiceUnitTests", + srcs: ["src/**/*.kt"], + static_libs: [ + "androidx.test.rules", + "androidx.test.runner", + "junit", + "services.core", + "truth-prebuilt", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml new file mode 100644 index 0000000000000..2ef7a1f56e765 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml new file mode 100644 index 0000000000000..78dd1c5f69901 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml @@ -0,0 +1,30 @@ + + + + + diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt new file mode 100644 index 0000000000000..2ff7baf1f5708 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationCoreApiTest.kt @@ -0,0 +1,174 @@ +/* + * 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.test.domain.verify + +import android.content.pm.domain.verify.DomainVerificationRequest +import android.content.pm.domain.verify.DomainVerificationSet +import android.content.pm.domain.verify.DomainVerificationUserSelection +import android.os.Parcel +import android.os.Parcelable +import android.os.UserHandle +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.util.UUID + +@RunWith(Parameterized::class) +class DomainVerificationCoreApiTest { + + companion object { + private val IS_EQUAL_TO: (value: Any, other: Any) -> Unit = { value, other -> + assertThat(value).isEqualTo(other) + } + private val IS_MAP_EQUAL_TO: (value: Map<*, *>, other: Map<*, *>) -> Unit = { value, + other -> + assertThat(value).containsExactlyEntriesIn(other) + } + + @JvmStatic + @Parameterized.Parameters + fun parameters() = arrayOf( + Parameter( + initial = { + DomainVerificationRequest( + setOf( + "com.test.pkg.one", + "com.test.pkg.two" + ) + ) + }, + unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll>(first, second, + { it.packageNames }, { it.component1() }) { value, other -> + assertThat(value).containsExactlyElementsIn(other) + } + } + ), + Parameter( + initial = { + DomainVerificationSet( + UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), + "com.test.pkg", + mapOf( + "example.com" to 0, + "example.org" to 1, + "example.new" to 1000 + ) + ) + }, + unparcel = { DomainVerificationSet.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll(first, second, + { it.identifier }, { it.component1() }, IS_EQUAL_TO + ) + assertAll(first, second, + { it.packageName }, { it.component2() }, IS_EQUAL_TO + ) + assertAll>(first, second, + { it.hostToStateMap }, { it.component3() }, IS_MAP_EQUAL_TO + ) + } + ), + Parameter( + initial = { + DomainVerificationUserSelection( + UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), + "com.test.pkg", + UserHandle.of(10), + true, + mapOf( + "example.com" to true, + "example.org" to false, + "example.new" to true + ) + ) + }, + unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll(first, second, + { it.identifier }, { it.component1() }, IS_EQUAL_TO + ) + assertAll(first, second, + { it.packageName }, { it.component2() }, IS_EQUAL_TO + ) + assertAll(first, second, + { it.user }, { it.component3() }, IS_EQUAL_TO + ) + assertAll( + first, second, { it.isLinkHandlingAllowed }, + { it.component4() }, IS_EQUAL_TO + ) + assertAll>( + first, second, { it.hostToUserSelectionMap }, + { it.component5() }, IS_MAP_EQUAL_TO + ) + } + ) + ) + + class Parameter( + val initial: () -> T, + val unparcel: (Parcel) -> T, + private val assertion: (first: T, second: T) -> Unit + ) { + @Suppress("UNCHECKED_CAST") + fun assert(first: Any, second: Any) = assertion(first as T, second as T) + } + + private fun assertAll(vararg values: T, block: (value: T, other: T) -> Unit) { + values.indices.drop(1).forEach { + block(values[0], values[it]) + } + } + + private fun assertAll( + first: T, + second: T, + fieldValue: (T) -> V, + componentValue: (T) -> V, + assertion: (value: V, other: V) -> Unit + ) { + val values = arrayOf(fieldValue(first), fieldValue(second), + componentValue(first), componentValue(second)) + values.indices.drop(1).forEach { + @Suppress("UNCHECKED_CAST") + assertion(values[0] as V, values[it] as V) + } + } + } + + @Parameterized.Parameter(0) + lateinit var parameter: Parameter<*> + + @Test + fun parcel() { + val parcel = Parcel.obtain() + val initial = parameter.initial() + initial.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + + val newInitial = parameter.initial() + val unparceled = parameter.unparcel(parcel) + parameter.assert(newInitial, unparceled) + + assertAll(initial, newInitial, unparceled) { value: Any, other: Any -> + assertThat(value).isEqualTo(other) + } + } +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt new file mode 100644 index 0000000000000..6997c78199cec --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/domain/verify/DomainVerificationModelExtensions.kt @@ -0,0 +1,33 @@ +/* + * 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 com.android.server.pm.test.domain.verify + +import android.content.pm.domain.verify.DomainVerificationRequest +import android.content.pm.domain.verify.DomainVerificationSet +import android.content.pm.domain.verify.DomainVerificationUserSelection + +operator fun DomainVerificationRequest.component1() = packageNames + +operator fun DomainVerificationSet.component1() = identifier +operator fun DomainVerificationSet.component2() = packageName +operator fun DomainVerificationSet.component3() = hostToStateMap + +operator fun DomainVerificationUserSelection.component1() = identifier +operator fun DomainVerificationUserSelection.component2() = packageName +operator fun DomainVerificationUserSelection.component3() = user +operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed +operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap