diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java index a166675b4e..2cfda9eda6 100644 --- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java +++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java @@ -62,6 +62,17 @@ public final class ConnectivityCompatChanges { // This was a platform change ID with value 191844585L before T public static final long RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER = 235355681L; + /** + * The self certified capabilities check should be enabled after android 13. + * + *

See {@link android.net.NetworkCapabilities} for more details. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION = 266524688; + private ConnectivityCompatChanges() { } } diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index e32ea8f7d5..3a38f454bd 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -113,6 +113,7 @@ import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; +import android.app.compat.CompatChanges; import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -121,6 +122,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.res.XmlResourceParser; import android.database.ContentObserver; import android.net.CaptivePortal; import android.net.CaptivePortalData; @@ -195,6 +197,7 @@ import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.VpnTransportInfo; +import android.net.connectivity.ConnectivityCompatChanges; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netd.aidl.NativeUidRangeConfig; @@ -269,6 +272,7 @@ import com.android.networkstack.apishim.BroadcastOptionsShimImpl; import com.android.networkstack.apishim.ConstantsShim; import com.android.networkstack.apishim.common.BroadcastOptionsShim; import com.android.networkstack.apishim.common.UnsupportedApiLevelException; +import com.android.server.connectivity.ApplicationSelfCertifiedNetworkCapabilities; import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker; import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker.AutomaticOnOffKeepalive; @@ -279,6 +283,7 @@ import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; import com.android.server.connectivity.DscpPolicyTracker; import com.android.server.connectivity.FullScore; +import com.android.server.connectivity.InvalidTagException; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; @@ -300,6 +305,8 @@ import com.android.server.connectivity.wear.CompanionDeviceManagerProxyService; import libcore.io.IoUtils; +import org.xmlpull.v1.XmlPullParserException; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -902,6 +909,13 @@ public class ConnectivityService extends IConnectivityManager.Stub // Only the handler thread is allowed to access this field. private long mIngressRateLimit = -1; + // This is the cache for the packageName -> ApplicationSelfCertifiedNetworkCapabilities. This + // value can be accessed from both handler thread and any random binder thread. Therefore, + // accessing this value requires holding a lock. + @GuardedBy("mSelfCertifiedCapabilityCache") + private final Map + mSelfCertifiedCapabilityCache = new HashMap<>(); + /** * Implements support for the legacy "one network per network type" model. * @@ -1452,6 +1466,20 @@ public class ConnectivityService extends IConnectivityManager.Stub public BroadcastOptionsShim makeBroadcastOptionsShim(BroadcastOptions options) { return BroadcastOptionsShimImpl.newInstance(options); } + + /** + * Wrapper method for + * {@link android.app.compat.CompatChanges#isChangeEnabled(long, String, UserHandle)}. + * + * @param changeId The ID of the compatibility change in question. + * @param packageName The package name of the app in question. + * @param user The user that the operation is done for. + * @return {@code true} if the change is enabled for the specified package. + */ + public boolean isChangeEnabled(long changeId, @NonNull final String packageName, + @NonNull final UserHandle user) { + return CompatChanges.isChangeEnabled(changeId, packageName, user); + } } public ConnectivityService(Context context) { @@ -6319,6 +6347,11 @@ public class ConnectivityService extends IConnectivityManager.Stub if (isMappedInOemNetworkPreference(packageName)) { handleSetOemNetworkPreference(mOemNetworkPreferences, null); } + + // Invalidates cache entry when the package is updated. + synchronized (mSelfCertifiedCapabilityCache) { + mSelfCertifiedCapabilityCache.remove(packageName); + } } private final BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { @@ -6947,8 +6980,69 @@ public class ConnectivityService extends IConnectivityManager.Stub asUid, requests, nr, msgr, binder, callbackFlags, callingAttributionTag); } + private boolean shouldCheckCapabilitiesDeclaration( + @NonNull final NetworkCapabilities networkCapabilities, final int callingUid, + @NonNull final String callingPackageName) { + final UserHandle user = UserHandle.getUserHandleForUid(callingUid); + // Only run the check if the change is enabled. + if (!mDeps.isChangeEnabled( + ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION, + callingPackageName, user)) { + return false; + } + + return networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + || networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY); + } + + private void enforceRequestCapabilitiesDeclaration(@NonNull final String callerPackageName, + @NonNull final NetworkCapabilities networkCapabilities) { + // This check is added to fix the linter error for "current min is 30", which is not going + // to happen because Connectivity service always run in S+. + if (!SdkLevel.isAtLeastS()) { + Log.wtf(TAG, "Connectivity service should always run in at least SDK S"); + return; + } + ApplicationSelfCertifiedNetworkCapabilities applicationNetworkCapabilities; + try { + synchronized (mSelfCertifiedCapabilityCache) { + applicationNetworkCapabilities = mSelfCertifiedCapabilityCache.get( + callerPackageName); + if (applicationNetworkCapabilities == null) { + final PackageManager packageManager = mContext.getPackageManager(); + final PackageManager.Property networkSliceProperty = packageManager.getProperty( + ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES, + callerPackageName + ); + final XmlResourceParser parser = packageManager + .getResourcesForApplication(callerPackageName) + .getXml(networkSliceProperty.getResourceId()); + applicationNetworkCapabilities = + ApplicationSelfCertifiedNetworkCapabilities.createFromXml(parser); + mSelfCertifiedCapabilityCache.put(callerPackageName, + applicationNetworkCapabilities); + } + + } + } catch (PackageManager.NameNotFoundException ne) { + throw new SecurityException( + "Cannot find " + ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES + + " property"); + } catch (XmlPullParserException | IOException | InvalidTagException e) { + throw new SecurityException(e.getMessage()); + } + + applicationNetworkCapabilities.enforceSelfCertifiedNetworkCapabilitiesDeclared( + networkCapabilities); + } private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities, String callingPackageName, String callingAttributionTag, final int callingUid) { + if (shouldCheckCapabilitiesDeclaration(networkCapabilities, callingUid, + callingPackageName)) { + enforceRequestCapabilitiesDeclaration(callingPackageName, networkCapabilities); + } if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) { // For T+ devices, callers with carrier privilege could request with CBS capabilities. if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS) diff --git a/service/src/com/android/server/connectivity/ApplicationSelfCertifiedNetworkCapabilities.java b/service/src/com/android/server/connectivity/ApplicationSelfCertifiedNetworkCapabilities.java new file mode 100644 index 0000000000..76e966f921 --- /dev/null +++ b/service/src/com/android/server/connectivity/ApplicationSelfCertifiedNetworkCapabilities.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2023 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.connectivity; + + +import android.annotation.NonNull; +import android.net.NetworkCapabilities; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayDeque; + + +/** + * The class for parsing and checking the self-declared application network capabilities. + * + * ApplicationSelfCertifiedNetworkCapabilities is an immutable class that + * can parse the self-declared application network capabilities in the application resources. The + * class also provides a helper method to check whether the requested network capabilities + * already self-declared. + */ +public final class ApplicationSelfCertifiedNetworkCapabilities { + + public static final String PRIORITIZE_LATENCY = "NET_CAPABILITY_PRIORITIZE_LATENCY"; + public static final String PRIORITIZE_BANDWIDTH = "NET_CAPABILITY_PRIORITIZE_BANDWIDTH"; + + private static final String TAG = + ApplicationSelfCertifiedNetworkCapabilities.class.getSimpleName(); + private static final String NETWORK_CAPABILITIES_DECLARATION_TAG = + "network-capabilities-declaration"; + private static final String USES_NETWORK_CAPABILITY_TAG = "uses-network-capability"; + private static final String NAME_TAG = "name"; + + private long mRequestedNetworkCapabilities = 0; + + /** + * Creates {@link ApplicationSelfCertifiedNetworkCapabilities} from a xml parser. + * + *

Here is an example of the xml syntax: + * + *

+     * {@code
+     *  
+     *     
+     *     
+     * 
+     * }
+     * 
+ *

+ * + * @param xmlParser The underlying {@link XmlPullParser} that will read the xml. + * @return An ApplicationSelfCertifiedNetworkCapabilities object. + * @throws InvalidTagException if the capabilities in xml config contains invalid tag. + * @throws XmlPullParserException if xml parsing failed. + * @throws IOException if unable to read the xml file properly. + */ + @NonNull + public static ApplicationSelfCertifiedNetworkCapabilities createFromXml( + @NonNull final XmlPullParser xmlParser) + throws InvalidTagException, XmlPullParserException, IOException { + return new ApplicationSelfCertifiedNetworkCapabilities(parseXml(xmlParser)); + } + + private static long parseXml(@NonNull final XmlPullParser xmlParser) + throws InvalidTagException, XmlPullParserException, IOException { + long requestedNetworkCapabilities = 0; + final ArrayDeque openTags = new ArrayDeque<>(); + + while (checkedNextTag(xmlParser, openTags) != XmlPullParser.START_TAG) { + continue; + } + + // Validates the tag is "network-capabilities-declaration" + if (!xmlParser.getName().equals(NETWORK_CAPABILITIES_DECLARATION_TAG)) { + throw new InvalidTagException("Invalid tag: " + xmlParser.getName()); + } + + checkedNextTag(xmlParser, openTags); + int eventType = xmlParser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + switch (eventType) { + case XmlPullParser.START_TAG: + // USES_NETWORK_CAPABILITY_TAG should directly be declared under + // NETWORK_CAPABILITIES_DECLARATION_TAG. + if (xmlParser.getName().equals(USES_NETWORK_CAPABILITY_TAG) + && openTags.size() == 1) { + int capability = parseDeclarationTag(xmlParser); + if (capability >= 0) { + requestedNetworkCapabilities |= 1L << capability; + } + } else { + Log.w(TAG, "Unknown tag: " + xmlParser.getName() + " ,tags stack size: " + + openTags.size()); + } + break; + default: + break; + } + eventType = checkedNextTag(xmlParser, openTags); + } + // Checks all the tags are parsed. + if (!openTags.isEmpty()) { + throw new InvalidTagException("Unbalanced tag: " + openTags.peek()); + } + return requestedNetworkCapabilities; + } + + private static int parseDeclarationTag(@NonNull final XmlPullParser xmlParser) { + String name = null; + for (int i = 0; i < xmlParser.getAttributeCount(); i++) { + final String attrName = xmlParser.getAttributeName(i); + if (attrName.equals(NAME_TAG)) { + name = xmlParser.getAttributeValue(i); + } else { + Log.w(TAG, "Unknown attribute name: " + attrName); + } + } + if (name != null) { + switch (name) { + case PRIORITIZE_BANDWIDTH: + return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH; + case PRIORITIZE_LATENCY: + return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY; + default: + Log.w(TAG, "Unknown capability declaration name: " + name); + } + } else { + Log.w(TAG, "uses-network-capability name must be specified"); + } + // Invalid capability + return -1; + } + + private static int checkedNextTag(@NonNull final XmlPullParser xmlParser, + @NonNull final ArrayDeque openTags) + throws XmlPullParserException, IOException, InvalidTagException { + if (xmlParser.getEventType() == XmlPullParser.START_TAG) { + openTags.addFirst(xmlParser.getName()); + } else if (xmlParser.getEventType() == XmlPullParser.END_TAG) { + if (!openTags.isEmpty() && openTags.peekFirst().equals(xmlParser.getName())) { + openTags.removeFirst(); + } else { + throw new InvalidTagException("Unbalanced tag: " + xmlParser.getName()); + } + } + return xmlParser.next(); + } + + private ApplicationSelfCertifiedNetworkCapabilities(long requestedNetworkCapabilities) { + mRequestedNetworkCapabilities = requestedNetworkCapabilities; + } + + /** + * Enforces self-certified capabilities are declared. + * + * @param networkCapabilities the input NetworkCapabilities to check against. + * @throws SecurityException if the capabilities are not properly self-declared. + */ + public void enforceSelfCertifiedNetworkCapabilitiesDeclared( + @NonNull final NetworkCapabilities networkCapabilities) { + if (networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + && !hasPrioritizeBandwidth()) { + throw new SecurityException( + "Missing " + ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_BANDWIDTH + + " declaration"); + } + if (networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + && !hasPrioritizeLatency()) { + throw new SecurityException( + "Missing " + ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_LATENCY + + " declaration"); + } + } + + /** + * Checks if NET_CAPABILITY_PRIORITIZE_LATENCY is declared. + */ + private boolean hasPrioritizeLatency() { + return (mRequestedNetworkCapabilities & (1L + << NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)) != 0; + } + + /** + * Checks if NET_CAPABILITY_PRIORITIZE_BANDWIDTH is declared. + */ + private boolean hasPrioritizeBandwidth() { + return (mRequestedNetworkCapabilities & (1L + << NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)) != 0; + } +} diff --git a/service/src/com/android/server/connectivity/InvalidTagException.java b/service/src/com/android/server/connectivity/InvalidTagException.java new file mode 100644 index 0000000000..b924d27584 --- /dev/null +++ b/service/src/com/android/server/connectivity/InvalidTagException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 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.connectivity; + + +/** + * An exception thrown when a Tag is not valid in self_certified_network_capabilities.xml. + */ +public class InvalidTagException extends Exception { + + public InvalidTagException(String message) { + super(message); + } +} diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp index ff9bd3193f..891c2ddb0a 100644 --- a/tests/cts/hostside/Android.bp +++ b/tests/cts/hostside/Android.bp @@ -47,6 +47,9 @@ java_test_host { data: [ ":CtsHostsideNetworkTestsApp", ":CtsHostsideNetworkTestsApp2", + ":CtsHostsideNetworkCapTestsAppWithoutProperty", + ":CtsHostsideNetworkCapTestsAppWithProperty", + ":CtsHostsideNetworkCapTestsAppSdk33", ] + next_app_data, per_testcase_directory: true, } diff --git a/tests/cts/hostside/networkslicingtestapp/Android.bp b/tests/cts/hostside/networkslicingtestapp/Android.bp new file mode 100644 index 0000000000..2aa3f6983f --- /dev/null +++ b/tests/cts/hostside/networkslicingtestapp/Android.bp @@ -0,0 +1,65 @@ +// +// Copyright (C) 2023 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_defaults { + name: "CtsHostsideNetworkCapTestsAppDefaults", + platform_apis: true, + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.rules", + "modules-utils-build", + "cts-net-utils", + ], + srcs: ["src/**/*.java"], + // Tag this module as a cts test artifact + test_suites: [ + "cts", + "general-tests", + "sts", + ], +} + +android_test_helper_app { + name: "CtsHostsideNetworkCapTestsAppWithoutProperty", + defaults: [ + "cts_support_defaults", + "CtsHostsideNetworkCapTestsAppDefaults" + ], + manifest: "AndroidManifestWithoutProperty.xml", +} + +android_test_helper_app { + name: "CtsHostsideNetworkCapTestsAppWithProperty", + defaults: [ + "cts_support_defaults", + "CtsHostsideNetworkCapTestsAppDefaults" + ], + manifest: "AndroidManifestWithProperty.xml", +} + +android_test_helper_app { + name: "CtsHostsideNetworkCapTestsAppSdk33", + defaults: [ + "cts_support_defaults", + "CtsHostsideNetworkCapTestsAppDefaults" + ], + target_sdk_version: "33", + manifest: "AndroidManifestWithoutProperty.xml", +} diff --git a/tests/cts/hostside/networkslicingtestapp/AndroidManifestWithProperty.xml b/tests/cts/hostside/networkslicingtestapp/AndroidManifestWithProperty.xml new file mode 100644 index 0000000000..3ef0376682 --- /dev/null +++ b/tests/cts/hostside/networkslicingtestapp/AndroidManifestWithProperty.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/tests/cts/hostside/networkslicingtestapp/AndroidManifestWithoutProperty.xml b/tests/cts/hostside/networkslicingtestapp/AndroidManifestWithoutProperty.xml new file mode 100644 index 0000000000..fe66684477 --- /dev/null +++ b/tests/cts/hostside/networkslicingtestapp/AndroidManifestWithoutProperty.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/tests/cts/hostside/networkslicingtestapp/res/xml/self_certified_network_capabilities_both.xml b/tests/cts/hostside/networkslicingtestapp/res/xml/self_certified_network_capabilities_both.xml new file mode 100644 index 0000000000..4066be239e --- /dev/null +++ b/tests/cts/hostside/networkslicingtestapp/res/xml/self_certified_network_capabilities_both.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/tests/cts/hostside/networkslicingtestapp/src/com.android.cts.net.hostside.networkslicingtestapp/NetworkSelfDeclaredCapabilitiesTest.java b/tests/cts/hostside/networkslicingtestapp/src/com.android.cts.net.hostside.networkslicingtestapp/NetworkSelfDeclaredCapabilitiesTest.java new file mode 100644 index 0000000000..39792fc3a0 --- /dev/null +++ b/tests/cts/hostside/networkslicingtestapp/src/com.android.cts.net.hostside.networkslicingtestapp/NetworkSelfDeclaredCapabilitiesTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 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.cts.net.hostside.networkslicingtestapp; + +import static org.junit.Assert.assertThrows; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.os.Build; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class NetworkSelfDeclaredCapabilitiesTest { + + @Rule + public final DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + public void requestNetwork_withoutRequestCapabilities() { + final ConnectivityManager cm = + (ConnectivityManager) + InstrumentationRegistry.getInstrumentation() + .getContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkRequest request = + new NetworkRequest.Builder().build(); + final ConnectivityManager.NetworkCallback callback = + new ConnectivityManager.NetworkCallback(); + cm.requestNetwork(request, callback); + cm.unregisterNetworkCallback(callback); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + public void requestNetwork_withSelfDeclaredCapabilities() { + final ConnectivityManager cm = + (ConnectivityManager) + InstrumentationRegistry.getInstrumentation() + .getContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkRequest request = + new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .build(); + final ConnectivityManager.NetworkCallback callback = + new ConnectivityManager.NetworkCallback(); + cm.requestNetwork(request, callback); + cm.unregisterNetworkCallback(callback); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + public void requestNetwork_lackingRequiredSelfDeclaredCapabilities() { + final ConnectivityManager cm = + (ConnectivityManager) + InstrumentationRegistry.getInstrumentation() + .getContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkRequest request = + new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .build(); + final ConnectivityManager.NetworkCallback callback = + new ConnectivityManager.NetworkCallback(); + assertThrows( + SecurityException.class, + () -> cm.requestNetwork(request, callback)); + } + +} diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideSelfDeclaredNetworkCapabilitiesCheckTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideSelfDeclaredNetworkCapabilitiesCheckTest.java new file mode 100644 index 0000000000..4c2985d4f0 --- /dev/null +++ b/tests/cts/hostside/src/com/android/cts/net/HostsideSelfDeclaredNetworkCapabilitiesCheckTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.cts.net; + +public class HostsideSelfDeclaredNetworkCapabilitiesCheckTest extends HostsideNetworkTestCase { + + private static final String TEST_WITH_PROPERTY_IN_CURRENT_SDK_APK = + "CtsHostsideNetworkCapTestsAppWithProperty.apk"; + private static final String TEST_WITHOUT_PROPERTY_IN_CURRENT_SDK_APK = + "CtsHostsideNetworkCapTestsAppWithoutProperty.apk"; + private static final String TEST_IN_SDK_33_APK = + "CtsHostsideNetworkCapTestsAppSdk33.apk"; + private static final String TEST_APP_PKG = + "com.android.cts.net.hostside.networkslicingtestapp"; + private static final String TEST_CLASS_NAME = ".NetworkSelfDeclaredCapabilitiesTest"; + private static final String WITH_SELF_DECLARED_CAPABILITIES_METHOD = + "requestNetwork_withSelfDeclaredCapabilities"; + private static final String LACKING_SELF_DECLARED_CAPABILITIES_METHOD = + "requestNetwork_lackingRequiredSelfDeclaredCapabilities"; + private static final String WITHOUT_REQUEST_CAPABILITIES_METHOD = + "requestNetwork_withoutRequestCapabilities"; + + + public void testRequestNetworkInCurrentSdkWithProperty() throws Exception { + uninstallPackage(TEST_APP_PKG, false); + installPackage(TEST_WITH_PROPERTY_IN_CURRENT_SDK_APK); + // If the self-declared capabilities are defined, + // the ConnectivityManager.requestNetwork() call should always pass. + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + WITH_SELF_DECLARED_CAPABILITIES_METHOD); + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + WITHOUT_REQUEST_CAPABILITIES_METHOD); + uninstallPackage(TEST_APP_PKG, true); + } + + public void testRequestNetworkInCurrentSdkWithoutProperty() throws Exception { + uninstallPackage(TEST_APP_PKG, false); + installPackage(TEST_WITHOUT_PROPERTY_IN_CURRENT_SDK_APK); + // If the self-declared capabilities are not defined, + // the ConnectivityManager.requestNetwork() call will fail if the properly is not declared. + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + LACKING_SELF_DECLARED_CAPABILITIES_METHOD); + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + WITHOUT_REQUEST_CAPABILITIES_METHOD); + uninstallPackage(TEST_APP_PKG, true); + } + + public void testRequestNetworkInSdk33() throws Exception { + uninstallPackage(TEST_APP_PKG, false); + installPackage(TEST_IN_SDK_33_APK); + // In Sdk33, the ConnectivityManager.requestNetwork() call should always pass. + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + WITH_SELF_DECLARED_CAPABILITIES_METHOD); + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + WITHOUT_REQUEST_CAPABILITIES_METHOD); + uninstallPackage(TEST_APP_PKG, true); + } + + public void testReinstallPackageWillUpdateProperty() throws Exception { + uninstallPackage(TEST_APP_PKG, false); + installPackage(TEST_WITHOUT_PROPERTY_IN_CURRENT_SDK_APK); + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + LACKING_SELF_DECLARED_CAPABILITIES_METHOD); + uninstallPackage(TEST_APP_PKG, true); + + + // Updates package. + installPackage(TEST_WITH_PROPERTY_IN_CURRENT_SDK_APK); + runDeviceTests(TEST_APP_PKG, + TEST_APP_PKG + TEST_CLASS_NAME, + WITH_SELF_DECLARED_CAPABILITIES_METHOD); + uninstallPackage(TEST_APP_PKG, true); + + } +} + diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 5b98237a66..1a358b252f 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -183,6 +183,8 @@ import static com.android.testutils.RecorderCallback.CallbackEntry.SUSPENDED; import static com.android.testutils.RecorderCallback.CallbackEntry.UNAVAILABLE; import static com.android.testutils.TestPermissionUtil.runAsShell; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -228,6 +230,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.usage.NetworkStatsManager; +import android.compat.testing.PlatformCompatChangeRule; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; @@ -312,6 +315,7 @@ import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.VpnTransportInfo; +import android.net.connectivity.ConnectivityCompatChanges; import android.net.metrics.IpConnectivityLog; import android.net.netd.aidl.NativeUidRangeConfig; import android.net.networkstack.NetworkStackClientBase; @@ -379,6 +383,7 @@ import com.android.networkstack.apishim.common.UnsupportedApiLevelException; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.ConnectivityService.NetworkRequestInfo; import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces; +import com.android.server.connectivity.ApplicationSelfCertifiedNetworkCapabilities; import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker; import com.android.server.connectivity.CarrierPrivilegeAuthenticator; import com.android.server.connectivity.ClatCoordinator; @@ -406,6 +411,9 @@ import com.android.testutils.RecorderCallback.CallbackEntry; import com.android.testutils.TestableNetworkCallback; import com.android.testutils.TestableNetworkOfferCallback; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -475,6 +483,9 @@ public class ConnectivityServiceTest { @Rule public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + @Rule + public final PlatformCompatChangeRule compatChangeRule = new PlatformCompatChangeRule(); + private static final int TIMEOUT_MS = 2_000; // Broadcasts can take a long time to be delivered. The test will not wait for that long unless // there is a failure, so use a long timeout. @@ -2105,6 +2116,37 @@ public class ConnectivityServiceTest { reset(mBroadcastOptionsShim); return mBroadcastOptionsShim; } + + @GuardedBy("this") + private boolean mForceDisableCompatChangeCheck = true; + + /** + * By default, the {@link #isChangeEnabled(long, String, UserHandle)} will always return + * true as the mForceDisableCompatChangeCheck is true and compat change check logic is + * never executed. The compat change check logic can be turned on by calling this method. + * If this method is called, the + * {@link libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges} or + * {@link libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges} must be + * used to turn on/off the compat change flag. + */ + private void enableCompatChangeCheck() { + synchronized (this) { + mForceDisableCompatChangeCheck = false; + } + } + + @Override + public boolean isChangeEnabled(long changeId, + @NonNull final String packageName, + @NonNull final UserHandle user) { + synchronized (this) { + if (mForceDisableCompatChangeCheck) { + return false; + } else { + return super.isChangeEnabled(changeId, packageName, user); + } + } + } } private class AutomaticOnOffKeepaliveTrackerDependencies @@ -6310,6 +6352,142 @@ public class ConnectivityServiceTest { } } + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @DisableCompatChanges(ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION) + public void testSelfCertifiedCapabilitiesDisabled() + throws Exception { + mDeps.enableCompatChangeCheck(); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .build(); + final TestNetworkCallback cb = new TestNetworkCallback(); + mCm.requestNetwork(networkRequest, cb); + mCm.unregisterNetworkCallback(cb); + } + + /** Set the networkSliceResourceId to 0 will result in NameNotFoundException be thrown. */ + private void setupMockForNetworkCapabilitiesResources(int networkSliceResourceId) + throws PackageManager.NameNotFoundException { + if (networkSliceResourceId == 0) { + doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getProperty( + ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES, + mContext.getPackageName()); + } else { + final PackageManager.Property property = new PackageManager.Property( + ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES, + networkSliceResourceId, + true /* isResource */, + mContext.getPackageName(), + "dummyClass" + ); + doReturn(property).when(mPackageManager).getProperty( + ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES, + mContext.getPackageName()); + doReturn(mContext.getResources()).when(mPackageManager).getResourcesForApplication( + mContext.getPackageName()); + } + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @EnableCompatChanges(ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION) + public void requestNetwork_withoutPrioritizeBandwidthDeclaration_shouldThrowException() + throws Exception { + mDeps.enableCompatChangeCheck(); + setupMockForNetworkCapabilitiesResources( + com.android.frameworks.tests.net.R.xml.self_certified_capabilities_latency); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .build(); + final TestNetworkCallback cb = new TestNetworkCallback(); + final Exception e = assertThrows(SecurityException.class, + () -> mCm.requestNetwork(networkRequest, cb)); + assertThat(e.getMessage(), + containsString(ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_BANDWIDTH)); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @EnableCompatChanges(ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION) + public void requestNetwork_withoutPrioritizeLatencyDeclaration_shouldThrowException() + throws Exception { + mDeps.enableCompatChangeCheck(); + setupMockForNetworkCapabilitiesResources( + com.android.frameworks.tests.net.R.xml.self_certified_capabilities_bandwidth); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .build(); + final TestNetworkCallback cb = new TestNetworkCallback(); + final Exception e = assertThrows(SecurityException.class, + () -> mCm.requestNetwork(networkRequest, cb)); + assertThat(e.getMessage(), + containsString(ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_LATENCY)); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @EnableCompatChanges(ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION) + public void requestNetwork_withoutNetworkSliceProperty_shouldThrowException() throws Exception { + mDeps.enableCompatChangeCheck(); + setupMockForNetworkCapabilitiesResources(0 /* networkSliceResourceId */); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .build(); + final TestNetworkCallback cb = new TestNetworkCallback(); + final Exception e = assertThrows(SecurityException.class, + () -> mCm.requestNetwork(networkRequest, cb)); + assertThat(e.getMessage(), + containsString(ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES)); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @EnableCompatChanges(ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION) + public void requestNetwork_withNetworkSliceDeclaration_shouldSucceed() throws Exception { + mDeps.enableCompatChangeCheck(); + setupMockForNetworkCapabilitiesResources( + com.android.frameworks.tests.net.R.xml.self_certified_capabilities_both); + + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .build(); + final TestNetworkCallback cb = new TestNetworkCallback(); + mCm.requestNetwork(networkRequest, cb); + mCm.unregisterNetworkCallback(cb); + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @EnableCompatChanges(ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION) + public void requestNetwork_withNetworkSliceDeclaration_shouldUseCache() throws Exception { + mDeps.enableCompatChangeCheck(); + setupMockForNetworkCapabilitiesResources( + com.android.frameworks.tests.net.R.xml.self_certified_capabilities_both); + + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + .build(); + final TestNetworkCallback cb = new TestNetworkCallback(); + mCm.requestNetwork(networkRequest, cb); + mCm.unregisterNetworkCallback(cb); + + // Second call should use caches + mCm.requestNetwork(networkRequest, cb); + mCm.unregisterNetworkCallback(cb); + + // PackageManager's API only called once because the second call is using cache. + verify(mPackageManager, times(1)).getProperty( + ConstantsShim.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES, + mContext.getPackageName()); + verify(mPackageManager, times(1)).getResourcesForApplication( + mContext.getPackageName()); + } + /** * Validate the service throws if request with CBS but without carrier privilege. */ diff --git a/tests/unit/java/com/android/server/connectivity/ApplicationSelfCertifiedNetworkCapabilitiesTest.kt b/tests/unit/java/com/android/server/connectivity/ApplicationSelfCertifiedNetworkCapabilitiesTest.kt new file mode 100644 index 0000000000..f2d7aaa077 --- /dev/null +++ b/tests/unit/java/com/android/server/connectivity/ApplicationSelfCertifiedNetworkCapabilitiesTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.connectivity + +import android.net.NetworkCapabilities +import android.os.Build +import androidx.test.InstrumentationRegistry +import androidx.test.filters.SmallTest +import com.android.frameworks.tests.net.R +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.google.common.truth.Truth.assertThat +import kotlin.test.assertFailsWith +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(DevSdkIgnoreRunner::class) +@SmallTest +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) +class ApplicationSelfCertifiedNetworkCapabilitiesTest { + private val mResource = InstrumentationRegistry.getContext().getResources() + private val bandwidthCapability = NetworkCapabilities.Builder().apply { + addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + }.build() + private val latencyCapability = NetworkCapabilities.Builder().apply { + addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + }.build() + private val emptyCapability = NetworkCapabilities.Builder().build() + private val bothCapabilities = NetworkCapabilities.Builder().apply { + addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) + addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) + }.build() + + @Test + fun parseXmlWithWrongTag_shouldIgnoreWrongTag() { + val parser = mResource.getXml( + R.xml.self_certified_capabilities_wrong_tag + ) + val selfDeclaredCaps = ApplicationSelfCertifiedNetworkCapabilities.createFromXml(parser) + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(latencyCapability) + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(bandwidthCapability) + } + + @Test + fun parseXmlWithWrongDeclaration_shouldThrowException() { + val parser = mResource.getXml( + R.xml.self_certified_capabilities_wrong_declaration + ) + val exception = assertFailsWith { + ApplicationSelfCertifiedNetworkCapabilities.createFromXml(parser) + } + assertThat(exception.message).contains("network-capabilities-declaration1") + } + + @Test + fun checkIfSelfCertifiedNetworkCapabilitiesDeclared_shouldThrowExceptionWhenNoDeclaration() { + val parser = mResource.getXml(R.xml.self_certified_capabilities_other) + val selfDeclaredCaps = ApplicationSelfCertifiedNetworkCapabilities.createFromXml(parser) + val exception1 = assertFailsWith { + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(latencyCapability) + } + assertThat(exception1.message).contains( + ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_LATENCY + ) + val exception2 = assertFailsWith { + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(bandwidthCapability) + } + assertThat(exception2.message).contains( + ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_BANDWIDTH + ) + } + + @Test + fun checkIfSelfCertifiedNetworkCapabilitiesDeclared_shouldPassIfDeclarationExist() { + val parser = mResource.getXml(R.xml.self_certified_capabilities_both) + val selfDeclaredCaps = ApplicationSelfCertifiedNetworkCapabilities.createFromXml(parser) + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(latencyCapability) + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(bandwidthCapability) + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(bothCapabilities) + selfDeclaredCaps.enforceSelfCertifiedNetworkCapabilitiesDeclared(emptyCapability) + } +} diff --git a/tests/unit/res/xml/self_certified_capabilities_bandwidth.xml b/tests/unit/res/xml/self_certified_capabilities_bandwidth.xml new file mode 100644 index 0000000000..01fdca1409 --- /dev/null +++ b/tests/unit/res/xml/self_certified_capabilities_bandwidth.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/tests/unit/res/xml/self_certified_capabilities_both.xml b/tests/unit/res/xml/self_certified_capabilities_both.xml new file mode 100644 index 0000000000..4066be239e --- /dev/null +++ b/tests/unit/res/xml/self_certified_capabilities_both.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/tests/unit/res/xml/self_certified_capabilities_latency.xml b/tests/unit/res/xml/self_certified_capabilities_latency.xml new file mode 100644 index 0000000000..1c4a0e0b85 --- /dev/null +++ b/tests/unit/res/xml/self_certified_capabilities_latency.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/tests/unit/res/xml/self_certified_capabilities_other.xml b/tests/unit/res/xml/self_certified_capabilities_other.xml new file mode 100644 index 0000000000..5b6649c778 --- /dev/null +++ b/tests/unit/res/xml/self_certified_capabilities_other.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/tests/unit/res/xml/self_certified_capabilities_wrong_declaration.xml b/tests/unit/res/xml/self_certified_capabilities_wrong_declaration.xml new file mode 100644 index 0000000000..6082356be1 --- /dev/null +++ b/tests/unit/res/xml/self_certified_capabilities_wrong_declaration.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/tests/unit/res/xml/self_certified_capabilities_wrong_tag.xml b/tests/unit/res/xml/self_certified_capabilities_wrong_tag.xml new file mode 100644 index 0000000000..c9ecc0b62c --- /dev/null +++ b/tests/unit/res/xml/self_certified_capabilities_wrong_tag.xml @@ -0,0 +1,21 @@ + + + + + + + +