Send domains through Parcel#writeBlob to avoid size limit

If the domains in each data class exceed 32 KB, serializes them using
writeBlob so that they get passed through shared memory rather than
the Binder transaction.

Bug: 177553185

Test: atest DomainVerificationCoreApiTest

Change-Id: I0dd4bd140c8b847eb604d8e42272ca5be5690731
This commit is contained in:
Winson
2021-02-10 17:23:35 -08:00
parent fdcfd2a2d5
commit 87f799c8bf
11 changed files with 581 additions and 144 deletions

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm.verify.domain;
parcelable DomainSet;

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm.verify.domain;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
import java.util.Set;
/**
* Wraps an input set of domains from the client process, to be sent to the server. Handles cases
* where the data size is too large by writing data using {@link Parcel#writeBlob(byte[])}.
*
* @hide
*/
@DataClass(genParcelable = true, genAidl = true, genEqualsHashCode = true)
public class DomainSet implements Parcelable {
@NonNull
private final Set<String> mDomains;
private void parcelDomains(@NonNull Parcel dest, @SuppressWarnings("unused") int flags) {
DomainVerificationUtils.writeHostSet(dest, mDomains);
}
private Set<String> unparcelDomains(@NonNull Parcel in) {
return DomainVerificationUtils.readHostSet(in);
}
// 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/verify/domain
// /DomainSet.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
@DataClass.Generated.Member
public DomainSet(
@NonNull Set<String> domains) {
this.mDomains = domains;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mDomains);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public @NonNull Set<String> getDomains() {
return mDomains;
}
@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(DomainSet other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
DomainSet that = (DomainSet) o;
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mDomains, that.mDomains);
}
@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(mDomains);
return _hash;
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
parcelDomains(dest, flags);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
protected DomainSet(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
Set<String> domains = unparcelDomains(in);
this.mDomains = domains;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mDomains);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<DomainSet> CREATOR
= new Parcelable.Creator<DomainSet>() {
@Override
public DomainSet[] newArray(int size) {
return new DomainSet[size];
}
@Override
public DomainSet createFromParcel(@NonNull Parcel in) {
return new DomainSet(in);
}
};
@DataClass.Generated(
time = 1613169242020L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainSet.java",
inputSignatures = "private final @android.annotation.NonNull java.util.Set<java.lang.String> mDomains\nprivate void parcelDomains(android.os.Parcel,int)\nprivate java.util.Set<java.lang.String> unparcelDomains(android.os.Parcel)\nclass DomainSet extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -19,7 +19,9 @@ package android.content.pm.verify.domain;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -34,12 +36,12 @@ import java.util.UUID;
* against the digital asset links response from the server hosting that domain.
* <p>
* These values for each domain can be modified through
* {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
* {@link DomainVerificationManager#setDomainVerificationStatus(UUID,
* Set, int)}.
*
* @hide
*/
@SystemApi
@SuppressWarnings("DefaultAnnotationParam")
@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
genEqualsHashCode = true)
public final class DomainVerificationInfo implements Parcelable {
@@ -71,22 +73,30 @@ public final class DomainVerificationInfo implements Parcelable {
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.
* 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.
* <p>
* 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.
* 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<String, Integer> mHostToStateMap;
private void parcelHostToStateMap(Parcel dest, @SuppressWarnings("unused") int flags) {
DomainVerificationUtils.writeHostMap(dest, mHostToStateMap);
}
private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
DomainVerificationUserSelection.class.getClassLoader());
}
// Code below generated by codegen v1.0.22.
@@ -95,7 +105,8 @@ public final class DomainVerificationInfo implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
// /DomainVerificationInfo.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -123,18 +134,17 @@ public final class DomainVerificationInfo implements Parcelable {
* @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.
* 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.
* <p>
* 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.
* 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
@@ -185,18 +195,17 @@ public final class DomainVerificationInfo implements Parcelable {
}
/**
* 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.
* 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.
* <p>
* 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.
* 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<String,Integer> getHostToStateMap() {
@@ -260,13 +269,13 @@ public final class DomainVerificationInfo implements Parcelable {
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
public void writeToParcel(@NonNull 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);
parcelHostToStateMap(dest, flags);
}
@Override
@@ -276,14 +285,13 @@ public final class DomainVerificationInfo implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ DomainVerificationInfo(@NonNull android.os.Parcel in) {
/* package-private */ DomainVerificationInfo(@NonNull 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<String,Integer> hostToStateMap = new java.util.LinkedHashMap<>();
in.readMap(hostToStateMap, Integer.class.getClassLoader());
Map<String,Integer> hostToStateMap = unparcelHostToStateMap(in);
this.mIdentifier = identifier;
com.android.internal.util.AnnotationValidations.validate(
@@ -307,16 +315,16 @@ public final class DomainVerificationInfo implements Parcelable {
}
@Override
public DomainVerificationInfo createFromParcel(@NonNull android.os.Parcel in) {
public DomainVerificationInfo createFromParcel(@NonNull Parcel in) {
return new DomainVerificationInfo(in);
}
};
@DataClass.Generated(
time = 1611862790369L,
time = 1613002530369L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.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<java.lang.String,java.lang.Integer> mHostToStateMap\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
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<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo 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() {}

View File

@@ -89,7 +89,7 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager
int state) throws IllegalArgumentException, NameNotFoundException {
try {
mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
new ArrayList<>(domains), state);
new DomainSet(domains), state);
} catch (Exception e) {
Exception converted = rethrow(e, domainSetId);
if (converted instanceof NameNotFoundException) {
@@ -126,7 +126,7 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager
throws IllegalArgumentException, NameNotFoundException {
try {
mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
new ArrayList<>(domains), enabled, mContext.getUserId());
new DomainSet(domains), enabled, mContext.getUserId());
} catch (Exception e) {
Exception converted = rethrow(e, domainSetId);
if (converted instanceof NameNotFoundException) {

View File

@@ -19,6 +19,7 @@ package android.content.pm.verify.domain;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
@@ -27,11 +28,11 @@ 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}.
* Request object sent in the {@link Intent} that's broadcast to the domain verification agent,
* retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}.
* <p>
* This contains the set of packages which have been invalidated and will require
* re-verification. The exact domains can be retrieved with
* This contains the set of packages which have been invalidated and will require re-verification.
* The exact domains can be retrieved with
* {@link DomainVerificationManager#getDomainVerificationInfo(String)}
*
* @hide
@@ -42,14 +43,22 @@ import java.util.Set;
public final class DomainVerificationRequest implements Parcelable {
/**
* The package names of the apps that need to be verified. The receiver should call
* {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
* these values to get the actual set of domains that need to be acted on.
* The package names of the apps that need to be verified. The receiver should call {@link
* DomainVerificationManager#getDomainVerificationInfo(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<String> mPackageNames;
private void parcelPackageNames(@NonNull Parcel dest, @SuppressWarnings("unused") int flags) {
DomainVerificationUtils.writeHostSet(dest, mPackageNames);
}
private Set<String> unparcelPackageNames(@NonNull Parcel in) {
return DomainVerificationUtils.readHostSet(in);
}
// Code below generated by codegen v1.0.22.
@@ -58,7 +67,8 @@ public final class DomainVerificationRequest implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
// /DomainVerificationRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -69,9 +79,9 @@ public final class DomainVerificationRequest implements Parcelable {
* Creates a new DomainVerificationRequest.
*
* @param packageNames
* The package names of the apps that need to be verified. The receiver should call
* {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
* these values to get the actual set of domains that need to be acted on.
* The package names of the apps that need to be verified. The receiver should call {@link
* DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get
* the actual set of domains that need to be acted on.
* @hide
*/
@DataClass.Generated.Member
@@ -85,9 +95,9 @@ public final class DomainVerificationRequest implements Parcelable {
}
/**
* The package names of the apps that need to be verified. The receiver should call
* {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
* these values to get the actual set of domains that need to be acted on.
* The package names of the apps that need to be verified. The receiver should call {@link
* DomainVerificationManager#getDomainVerificationInfo(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<String> getPackageNames() {
@@ -134,11 +144,11 @@ public final class DomainVerificationRequest implements Parcelable {
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
sParcellingForPackageNames.parcel(mPackageNames, dest, flags);
parcelPackageNames(dest, flags);
}
@Override
@@ -148,11 +158,11 @@ public final class DomainVerificationRequest implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) {
/* package-private */ DomainVerificationRequest(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
Set<String> packageNames = sParcellingForPackageNames.unparcel(in);
Set<String> packageNames = unparcelPackageNames(in);
this.mPackageNames = packageNames;
com.android.internal.util.AnnotationValidations.validate(
@@ -170,16 +180,16 @@ public final class DomainVerificationRequest implements Parcelable {
}
@Override
public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) {
public DomainVerificationRequest createFromParcel(@NonNull Parcel in) {
return new DomainVerificationRequest(in);
}
};
@DataClass.Generated(
time = 1611862814990L,
time = 1613169505495L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/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<java.lang.String> mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)")
inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nprivate void parcelPackageNames(android.os.Parcel,int)\nprivate java.util.Set<java.lang.String> unparcelPackageNames(android.os.Parcel)\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() {}

View File

@@ -20,8 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.util.ArrayMap;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -46,13 +48,12 @@ import java.util.UUID;
* <p>
* These values can be changed through the
* {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
* boolean)} and
* {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
* boolean)} and {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
* boolean)} APIs.
* <p>
* 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.
* 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
*/
@@ -88,16 +89,24 @@ public final class DomainVerificationUserSelection implements Parcelable {
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)}.
* 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<String, Boolean> mHostToUserSelectionMap;
private void parcelHostToUserSelectionMap(Parcel dest, @SuppressWarnings("unused") int flags) {
DomainVerificationUtils.writeHostMap(dest, mHostToUserSelectionMap);
}
private Map<String, Boolean> unparcelHostToUserSelectionMap(Parcel in) {
return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
DomainVerificationUserSelection.class.getClassLoader());
}
// Code below generated by codegen v1.0.22.
@@ -124,10 +133,9 @@ public final class DomainVerificationUserSelection implements Parcelable {
* @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)}.
* 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
@@ -189,10 +197,9 @@ public final class DomainVerificationUserSelection implements Parcelable {
}
/**
* Retrieve the existing user selection state for the matching
* {@link #getPackageName()}, as was previously set by
* {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
* boolean)}.
* 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.
*/
@@ -264,7 +271,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -274,7 +281,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
dest.writeString(mPackageName);
dest.writeTypedObject(mUser, flags);
dest.writeMap(mHostToUserSelectionMap);
parcelHostToUserSelectionMap(dest, flags);
}
@Override
@@ -284,7 +291,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) {
/* package-private */ DomainVerificationUserSelection(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -293,8 +300,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
UUID identifier = sParcellingForIdentifier.unparcel(in);
String packageName = in.readString();
UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR);
Map<String,Boolean> hostToUserSelectionMap = new java.util.LinkedHashMap<>();
in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader());
Map<String,Boolean> hostToUserSelectionMap = unparcelHostToUserSelectionMap(in);
this.mIdentifier = identifier;
com.android.internal.util.AnnotationValidations.validate(
@@ -324,16 +330,16 @@ public final class DomainVerificationUserSelection implements Parcelable {
}
@Override
public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) {
public DomainVerificationUserSelection createFromParcel(@NonNull Parcel in) {
return new DomainVerificationUserSelection(in);
}
};
@DataClass.Generated(
time = 1612829797220L,
time = 1613002353615L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/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<java.lang.String,java.lang.Boolean> 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)")
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<java.lang.String,java.lang.Boolean> mHostToUserSelectionMap\nprivate void parcelHostToUserSelectionMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Boolean> unparcelHostToUserSelectionMap(android.os.Parcel)\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() {}

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm.verify.domain;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.util.ArrayMap;
import android.util.ArraySet;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/** @hide */
public class DomainVerificationUtils {
private static final int STRINGS_TARGET_BYTE_SIZE = IBinder.getSuggestedMaxIpcSizeBytes() / 2;
/**
* Write a map containing web hosts to the given parcel, using {@link Parcel#writeBlob(byte[])}
* if the limit exceeds {@link IBinder#getSuggestedMaxIpcSizeBytes()} / 2. This assumes that
* the written map is the only data structure in the caller that varies based on the host data
* set. Other data that will be written to the parcel after this method will not be considered
* in the calculation.
*/
public static void writeHostMap(@NonNull Parcel dest, @NonNull Map<String, ?> map) {
boolean targetSizeExceeded = false;
int totalSize = dest.dataSize();
for (String host : map.keySet()) {
totalSize += estimatedByteSizeOf(host);
if (totalSize > STRINGS_TARGET_BYTE_SIZE) {
targetSizeExceeded = true;
break;
}
}
dest.writeBoolean(targetSizeExceeded);
if (!targetSizeExceeded) {
dest.writeMap(map);
return;
}
Parcel data = Parcel.obtain();
try {
data.writeMap(map);
dest.writeBlob(data.marshall());
} finally {
data.recycle();
}
}
/**
* Retrieve a map previously written by {@link #writeHostMap(Parcel, Map)}.
*/
@NonNull
@SuppressWarnings("rawtypes")
public static <T extends Map> T readHostMap(@NonNull Parcel in, @NonNull T map,
@NonNull ClassLoader classLoader) {
boolean targetSizeExceeded = in.readBoolean();
if (!targetSizeExceeded) {
in.readMap(map, classLoader);
return map;
}
Parcel data = Parcel.obtain();
try {
byte[] blob = in.readBlob();
data.unmarshall(blob, 0, blob.length);
data.setDataPosition(0);
data.readMap(map, classLoader);
} finally {
data.recycle();
}
return map;
}
/**
* {@link ArraySet} variant of {@link #writeHostMap(Parcel, Map)}.
*/
public static void writeHostSet(@NonNull Parcel dest, @NonNull Set<String> set) {
boolean targetSizeExceeded = false;
int totalSize = dest.dataSize();
for (String host : set) {
totalSize += estimatedByteSizeOf(host);
if (totalSize > STRINGS_TARGET_BYTE_SIZE) {
targetSizeExceeded = true;
break;
}
}
dest.writeBoolean(targetSizeExceeded);
if (!targetSizeExceeded) {
writeSet(dest, set);
return;
}
Parcel data = Parcel.obtain();
try {
writeSet(data, set);
dest.writeBlob(data.marshall());
} finally {
data.recycle();
}
}
/**
* {@link ArraySet} variant of {@link #readHostMap(Parcel, Map, ClassLoader)}.
*/
@NonNull
public static Set<String> readHostSet(@NonNull Parcel in) {
boolean targetSizeExceeded = in.readBoolean();
if (!targetSizeExceeded) {
return readSet(in);
}
Parcel data = Parcel.obtain();
try {
byte[] blob = in.readBlob();
data.unmarshall(blob, 0, blob.length);
data.setDataPosition(0);
return readSet(data);
} finally {
data.recycle();
}
}
private static void writeSet(@NonNull Parcel dest, @Nullable Set<String> set) {
if (set == null) {
dest.writeInt(-1);
return;
}
dest.writeInt(set.size());
for (String string : set) {
dest.writeString(string);
}
}
@NonNull
private static Set<String> readSet(@NonNull Parcel in) {
int size = in.readInt();
if (size == -1) {
return Collections.emptySet();
}
ArraySet<String> set = new ArraySet<>(size);
for (int count = 0; count < size; count++) {
set.add(in.readString());
}
return set;
}
/**
* Ballpark the size of domains to avoid unnecessary allocation of ashmem when sending domains
* across the client-server API.
*/
public static int estimatedByteSizeOf(String string) {
return string.length() * 2 + 12;
}
}

View File

@@ -16,6 +16,7 @@
package android.content.pm.verify.domain;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationUserSelection;
import java.util.List;
@@ -35,10 +36,10 @@ interface IDomainVerificationManager {
DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName,
int userId);
void setDomainVerificationStatus(String domainSetId, in List<String> domains, int state);
void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
void setDomainVerificationUserSelection(String domainSetId, in List<String> domains,
void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
boolean enabled, int userId);
}

View File

@@ -42,6 +42,8 @@ public class DomainVerificationCollector {
private static final Pattern DOMAIN_NAME_WITH_WILDCARD =
Pattern.compile("(\\*\\.)?" + Patterns.DOMAIN_NAME.pattern());
private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024;
@NonNull
private final PlatformCompat mPlatformCompat;
@@ -71,7 +73,7 @@ public class DomainVerificationCollector {
* <li>- Only IntentFilter.SCHEME_HTTP and/or IntentFilter.SCHEME_HTTPS,
* with no other schemes</li>
* </ul>
*
* <p>
* On prior versions of Android, Intent.CATEGORY_BROWSABLE was not a requirement, other
* schemes were allowed, and setting autoVerify to true in any intent filter would implicitly
* pretend that all intent filters were set to autoVerify="true".
@@ -86,8 +88,8 @@ public class DomainVerificationCollector {
}
/**
* Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires
* {@link IntentFilter#getAutoVerify()} == true.
* Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires {@link
* IntentFilter#getAutoVerify()} == true.
*/
@NonNull
public ArraySet<String> collectAutoVerifyDomains(@NonNull AndroidPackage pkg) {
@@ -100,24 +102,21 @@ public class DomainVerificationCollector {
boolean restrictDomains =
DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS);
ArraySet<String> domains = new ArraySet<>();
if (restrictDomains) {
collectDomains(domains, pkg, checkAutoVerify);
return collectDomainsInternal(pkg, checkAutoVerify);
} else {
collectDomainsLegacy(domains, pkg, checkAutoVerify);
return collectDomainsLegacy(pkg, checkAutoVerify);
}
return domains;
}
/** @see #RESTRICT_DOMAINS */
private void collectDomainsLegacy(@NonNull Set<String> domains,
@NonNull AndroidPackage pkg, boolean checkAutoVerify) {
/**
* @see #RESTRICT_DOMAINS
*/
private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg,
boolean checkAutoVerify) {
if (!checkAutoVerify) {
// Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2
collectDomains(domains, pkg, false);
return;
return collectDomainsInternal(pkg, false);
}
List<ParsedActivity> activities = pkg.getActivities();
@@ -140,39 +139,54 @@ public class DomainVerificationCollector {
}
if (!needsAutoVerify) {
return;
return new ArraySet<>();
}
}
for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
ArraySet<String> domains = new ArraySet<>();
int totalSize = 0;
boolean underMaxSize = true;
for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize;
activityIndex++) {
ParsedActivity activity = activities.get(activityIndex);
List<ParsedIntentInfo> intents = activity.getIntents();
int intentsSize = intents.size();
for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) {
ParsedIntentInfo intent = intents.get(intentIndex);
if (intent.handlesWebUris(false)) {
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
String host = intent.getDataAuthority(index).getHost();
if (isValidHost(host)) {
totalSize += byteSizeOf(host);
underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
domains.add(host);
}
}
}
}
}
return domains;
}
/** @see #RESTRICT_DOMAINS */
private void collectDomains(@NonNull Set<String> domains,
@NonNull AndroidPackage pkg, boolean checkAutoVerify) {
/**
* @see #RESTRICT_DOMAINS
*/
private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg,
boolean checkAutoVerify) {
ArraySet<String> domains = new ArraySet<>();
int totalSize = 0;
boolean underMaxSize = true;
List<ParsedActivity> activities = pkg.getActivities();
int activitiesSize = activities.size();
for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize;
activityIndex++) {
ParsedActivity activity = activities.get(activityIndex);
List<ParsedIntentInfo> intents = activity.getIntents();
int intentsSize = intents.size();
for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) {
ParsedIntentInfo intent = intents.get(intentIndex);
if (checkAutoVerify && !intent.getAutoVerify()) {
continue;
@@ -198,14 +212,27 @@ public class DomainVerificationCollector {
// app developer by declaring a separate intent-filter. This may not be worth
// fixing.
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
for (int index = 0; index < authorityCount && underMaxSize; index++) {
String host = intent.getDataAuthority(index).getHost();
if (isValidHost(host)) {
totalSize += byteSizeOf(host);
underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
domains.add(host);
}
}
}
}
return domains;
}
/**
* Ballpark the size of domains to avoid a ridiculous amount of domains that could slow
* down client-server communication.
*/
private int byteSizeOf(String string) {
// Use the same method from core for the data objects so that restrictions are consistent
return android.content.pm.verify.domain.DomainVerificationUtils.estimatedByteSizeOf(string);
}
/**

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationManagerImpl;
import android.content.pm.verify.domain.DomainVerificationInfo;
@@ -61,11 +62,11 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
}
@Override
public void setDomainVerificationStatus(String domainSetId, List<String> domains,
public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
int state) {
try {
mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
new ArraySet<>(domains), state);
domainSet.getDomains(), state);
} catch (Exception e) {
throw rethrow(e);
}
@@ -82,11 +83,11 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
}
@Override
public void setDomainVerificationUserSelection(String domainSetId, List<String> domains,
public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
boolean enabled, @UserIdInt int userId) {
try {
mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
new ArraySet<>(domains), enabled, userId);
domainSet.getDomains(), enabled, userId);
} catch (Exception e) {
throw rethrow(e);
}

View File

@@ -16,8 +16,9 @@
package com.android.server.pm.test.verify.domain
import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainSet
import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainVerificationUserSelection
import android.os.Parcel
import android.os.Parcelable
@@ -27,6 +28,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.util.UUID
import kotlin.random.Random
@RunWith(Parameterized::class)
class DomainVerificationCoreApiTest {
@@ -40,18 +42,25 @@ class DomainVerificationCoreApiTest {
assertThat(value).containsExactlyEntriesIn(other)
}
private val massiveSet by lazy {
val fragmentOf21 = ".com.example.test.app"
val list = mutableListOf("prefix$fragmentOf21")
var totalSize = 0
// Slightly overshoot a size of 1MB
while (totalSize < (1024 * 512)) {
val nextValue = "${list.last()}$fragmentOf21"
totalSize += nextValue.length
list += nextValue
}
list.toSet()
}
@JvmStatic
@Parameterized.Parameters
@Parameterized.Parameters(name = "{0}")
fun parameters() = arrayOf(
Parameter(
initial = {
DomainVerificationRequest(
setOf(
"com.test.pkg.one",
"com.test.pkg.two"
)
)
},
testName = "DomainVerificationRequest",
initial = { DomainVerificationRequest(massiveSet) },
unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) },
assertion = { first, second ->
assertAll<DomainVerificationRequest, Set<String>>(first, second,
@@ -61,15 +70,12 @@ class DomainVerificationCoreApiTest {
}
),
Parameter(
testName = "DomainVerificationInfo",
initial = {
DomainVerificationInfo(
UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
"com.test.pkg",
mapOf(
"example.com" to 0,
"example.org" to 1,
"example.new" to 1000
)
massiveSet.withIndex().associate { it.value to it.index }
)
},
unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
@@ -86,17 +92,15 @@ class DomainVerificationCoreApiTest {
}
),
Parameter(
testName = "DomainVerificationUserSelection",
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
)
massiveSet.withIndex()
.associate { it.value to (it.index % 2 == 0) }
)
},
unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) },
@@ -119,16 +123,30 @@ class DomainVerificationCoreApiTest {
{ it.component5() }, IS_MAP_EQUAL_TO
)
}
),
Parameter(
testName = "DomainSet",
initial = { DomainSet(massiveSet) },
unparcel = { DomainSet.CREATOR.createFromParcel(it) },
assertion = { first, second ->
assertAll<DomainSet, Set<String>>(
first, second,
{ it.domains }, assertion = IS_EQUAL_TO
)
}
)
)
class Parameter<T : Parcelable>(
val testName: String,
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)
override fun toString() = testName
}
private fun <T> assertAll(vararg values: T, block: (value: T, other: T) -> Unit) {
@@ -141,11 +159,17 @@ class DomainVerificationCoreApiTest {
first: T,
second: T,
fieldValue: (T) -> V,
componentValue: (T) -> V,
componentValue: ((T) -> V)? = null,
assertion: (value: V, other: V) -> Unit
) {
val values = arrayOf<Any>(fieldValue(first), fieldValue(second),
componentValue(first), componentValue(second))
val values = mutableListOf<Any>(fieldValue(first), fieldValue(second))
.apply {
componentValue?.let {
add(it(first))
add(it(second))
}
}
.toTypedArray()
values.indices.drop(1).forEach {
@Suppress("UNCHECKED_CAST")
assertion(values[0] as V, values[it] as V)