1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity; 18 19 20 import android.annotation.NonNull; 21 import android.net.NetworkCapabilities; 22 import android.util.Log; 23 24 import org.xmlpull.v1.XmlPullParser; 25 import org.xmlpull.v1.XmlPullParserException; 26 27 import java.io.IOException; 28 import java.util.ArrayDeque; 29 30 31 /** 32 * The class for parsing and checking the self-declared application network capabilities. 33 * 34 * ApplicationSelfCertifiedNetworkCapabilities is an immutable class that 35 * can parse the self-declared application network capabilities in the application resources. The 36 * class also provides a helper method to check whether the requested network capabilities 37 * already self-declared. 38 */ 39 public final class ApplicationSelfCertifiedNetworkCapabilities { 40 41 public static final String PRIORITIZE_LATENCY = "NET_CAPABILITY_PRIORITIZE_LATENCY"; 42 public static final String PRIORITIZE_BANDWIDTH = "NET_CAPABILITY_PRIORITIZE_BANDWIDTH"; 43 44 private static final String TAG = 45 ApplicationSelfCertifiedNetworkCapabilities.class.getSimpleName(); 46 private static final String NETWORK_CAPABILITIES_DECLARATION_TAG = 47 "network-capabilities-declaration"; 48 private static final String USES_NETWORK_CAPABILITY_TAG = "uses-network-capability"; 49 private static final String NAME_TAG = "name"; 50 51 private long mRequestedNetworkCapabilities = 0; 52 53 /** 54 * Creates {@link ApplicationSelfCertifiedNetworkCapabilities} from a xml parser. 55 * 56 * <p> Here is an example of the xml syntax: 57 * 58 * <pre> 59 * {@code 60 * <network-capabilities-declaration xmlns:android="http://schemas.android.com/apk/res/android"> 61 * <uses-network-capability android:name="NET_CAPABILITY_PRIORITIZE_LATENCY"/> 62 * <uses-network-capability android:name="NET_CAPABILITY_PRIORITIZE_BANDWIDTH"/> 63 * </network-capabilities-declaration> 64 * } 65 * </pre> 66 * <p> 67 * 68 * @param xmlParser The underlying {@link XmlPullParser} that will read the xml. 69 * @return An ApplicationSelfCertifiedNetworkCapabilities object. 70 * @throws InvalidTagException if the capabilities in xml config contains invalid tag. 71 * @throws XmlPullParserException if xml parsing failed. 72 * @throws IOException if unable to read the xml file properly. 73 */ 74 @NonNull createFromXml( @onNull final XmlPullParser xmlParser)75 public static ApplicationSelfCertifiedNetworkCapabilities createFromXml( 76 @NonNull final XmlPullParser xmlParser) 77 throws InvalidTagException, XmlPullParserException, IOException { 78 return new ApplicationSelfCertifiedNetworkCapabilities(parseXml(xmlParser)); 79 } 80 parseXml(@onNull final XmlPullParser xmlParser)81 private static long parseXml(@NonNull final XmlPullParser xmlParser) 82 throws InvalidTagException, XmlPullParserException, IOException { 83 long requestedNetworkCapabilities = 0; 84 final ArrayDeque<String> openTags = new ArrayDeque<>(); 85 86 while (checkedNextTag(xmlParser, openTags) != XmlPullParser.START_TAG) { 87 continue; 88 } 89 90 // Validates the tag is "network-capabilities-declaration" 91 if (!xmlParser.getName().equals(NETWORK_CAPABILITIES_DECLARATION_TAG)) { 92 throw new InvalidTagException("Invalid tag: " + xmlParser.getName()); 93 } 94 95 checkedNextTag(xmlParser, openTags); 96 int eventType = xmlParser.getEventType(); 97 while (eventType != XmlPullParser.END_DOCUMENT) { 98 switch (eventType) { 99 case XmlPullParser.START_TAG: 100 // USES_NETWORK_CAPABILITY_TAG should directly be declared under 101 // NETWORK_CAPABILITIES_DECLARATION_TAG. 102 if (xmlParser.getName().equals(USES_NETWORK_CAPABILITY_TAG) 103 && openTags.size() == 1) { 104 int capability = parseDeclarationTag(xmlParser); 105 if (capability >= 0) { 106 requestedNetworkCapabilities |= 1L << capability; 107 } 108 } else { 109 Log.w(TAG, "Unknown tag: " + xmlParser.getName() + " ,tags stack size: " 110 + openTags.size()); 111 } 112 break; 113 default: 114 break; 115 } 116 eventType = checkedNextTag(xmlParser, openTags); 117 } 118 // Checks all the tags are parsed. 119 if (!openTags.isEmpty()) { 120 throw new InvalidTagException("Unbalanced tag: " + openTags.peek()); 121 } 122 return requestedNetworkCapabilities; 123 } 124 parseDeclarationTag(@onNull final XmlPullParser xmlParser)125 private static int parseDeclarationTag(@NonNull final XmlPullParser xmlParser) { 126 String name = null; 127 for (int i = 0; i < xmlParser.getAttributeCount(); i++) { 128 final String attrName = xmlParser.getAttributeName(i); 129 if (attrName.equals(NAME_TAG)) { 130 name = xmlParser.getAttributeValue(i); 131 } else { 132 Log.w(TAG, "Unknown attribute name: " + attrName); 133 } 134 } 135 if (name != null) { 136 switch (name) { 137 case PRIORITIZE_BANDWIDTH: 138 return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH; 139 case PRIORITIZE_LATENCY: 140 return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY; 141 default: 142 Log.w(TAG, "Unknown capability declaration name: " + name); 143 } 144 } else { 145 Log.w(TAG, "uses-network-capability name must be specified"); 146 } 147 // Invalid capability 148 return -1; 149 } 150 checkedNextTag(@onNull final XmlPullParser xmlParser, @NonNull final ArrayDeque<String> openTags)151 private static int checkedNextTag(@NonNull final XmlPullParser xmlParser, 152 @NonNull final ArrayDeque<String> openTags) 153 throws XmlPullParserException, IOException, InvalidTagException { 154 if (xmlParser.getEventType() == XmlPullParser.START_TAG) { 155 openTags.addFirst(xmlParser.getName()); 156 } else if (xmlParser.getEventType() == XmlPullParser.END_TAG) { 157 if (!openTags.isEmpty() && openTags.peekFirst().equals(xmlParser.getName())) { 158 openTags.removeFirst(); 159 } else { 160 throw new InvalidTagException("Unbalanced tag: " + xmlParser.getName()); 161 } 162 } 163 return xmlParser.next(); 164 } 165 ApplicationSelfCertifiedNetworkCapabilities(long requestedNetworkCapabilities)166 private ApplicationSelfCertifiedNetworkCapabilities(long requestedNetworkCapabilities) { 167 mRequestedNetworkCapabilities = requestedNetworkCapabilities; 168 } 169 170 /** 171 * Enforces self-certified capabilities are declared. 172 * 173 * @param networkCapabilities the input NetworkCapabilities to check against. 174 * @throws SecurityException if the capabilities are not properly self-declared. 175 */ enforceSelfCertifiedNetworkCapabilitiesDeclared( @onNull final NetworkCapabilities networkCapabilities)176 public void enforceSelfCertifiedNetworkCapabilitiesDeclared( 177 @NonNull final NetworkCapabilities networkCapabilities) { 178 if (networkCapabilities.hasCapability( 179 NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH) 180 && !hasPrioritizeBandwidth()) { 181 throw new SecurityException( 182 "Missing " + ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_BANDWIDTH 183 + " declaration"); 184 } 185 if (networkCapabilities.hasCapability( 186 NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY) 187 && !hasPrioritizeLatency()) { 188 throw new SecurityException( 189 "Missing " + ApplicationSelfCertifiedNetworkCapabilities.PRIORITIZE_LATENCY 190 + " declaration"); 191 } 192 } 193 194 /** 195 * Checks if NET_CAPABILITY_PRIORITIZE_LATENCY is declared. 196 */ hasPrioritizeLatency()197 private boolean hasPrioritizeLatency() { 198 return (mRequestedNetworkCapabilities & (1L 199 << NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)) != 0; 200 } 201 202 /** 203 * Checks if NET_CAPABILITY_PRIORITIZE_BANDWIDTH is declared. 204 */ hasPrioritizeBandwidth()205 private boolean hasPrioritizeBandwidth() { 206 return (mRequestedNetworkCapabilities & (1L 207 << NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)) != 0; 208 } 209 } 210