1 /* 2 * Copyright (C) 2022 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.internal.pm.pkg.component; 18 19 import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; 20 21 import android.annotation.FlaggedApi; 22 import android.annotation.NonNull; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.UriRelativeFilter; 26 import android.content.UriRelativeFilterGroup; 27 import android.content.pm.Flags; 28 import android.content.pm.parsing.result.ParseInput; 29 import android.content.pm.parsing.result.ParseResult; 30 import android.content.res.Resources; 31 import android.content.res.TypedArray; 32 import android.content.res.XmlResourceParser; 33 import android.os.PatternMatcher; 34 import android.util.Slog; 35 import android.util.TypedValue; 36 37 import com.android.internal.R; 38 import com.android.internal.pm.pkg.parsing.ParsingPackage; 39 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; 40 import com.android.internal.pm.pkg.parsing.ParsingUtils; 41 42 import org.xmlpull.v1.XmlPullParser; 43 import org.xmlpull.v1.XmlPullParserException; 44 45 import java.io.IOException; 46 import java.util.Iterator; 47 48 /** @hide */ 49 public class ParsedIntentInfoUtils { 50 51 private static final String TAG = ParsingUtils.TAG; 52 53 public static final boolean DEBUG = false; 54 55 @NonNull parseIntentInfo(String className, ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, boolean allowAutoVerify, ParseInput input)56 public static ParseResult<ParsedIntentInfoImpl> parseIntentInfo(String className, 57 ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, 58 boolean allowAutoVerify, ParseInput input) 59 throws XmlPullParserException, IOException { 60 ParsedIntentInfoImpl intentInfo = new ParsedIntentInfoImpl(); 61 IntentFilter intentFilter = intentInfo.getIntentFilter(); 62 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter); 63 try { 64 intentFilter.setPriority( 65 sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0)); 66 intentFilter.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0)); 67 68 TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label); 69 if (v != null) { 70 intentInfo.setLabelRes(v.resourceId); 71 if (v.resourceId == 0) { 72 intentInfo.setNonLocalizedLabel(v.coerceToString()); 73 } 74 } 75 76 if (ParsingPackageUtils.sUseRoundIcon) { 77 intentInfo.setIcon(sa.getResourceId( 78 R.styleable.AndroidManifestIntentFilter_roundIcon, 0)); 79 } 80 81 if (intentInfo.getIcon() == 0) { 82 intentInfo.setIcon( 83 sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0)); 84 } 85 86 if (allowAutoVerify) { 87 intentFilter.setAutoVerify(sa.getBoolean( 88 R.styleable.AndroidManifestIntentFilter_autoVerify, 89 false)); 90 } 91 } finally { 92 sa.recycle(); 93 } 94 final int depth = parser.getDepth(); 95 int type; 96 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 97 && (type != XmlPullParser.END_TAG 98 || parser.getDepth() > depth)) { 99 if (type != XmlPullParser.START_TAG) { 100 continue; 101 } 102 if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) { 103 continue; 104 } 105 106 final ParseResult result; 107 String nodeName = parser.getName(); 108 switch (nodeName) { 109 case "action": { 110 String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name"); 111 if (value == null) { 112 result = input.error("No value supplied for <android:name>"); 113 } else if (value.isEmpty()) { 114 intentFilter.addAction(value); 115 // Prior to R, this was not a failure 116 result = input.deferError("No value supplied for <android:name>", 117 ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY); 118 } else { 119 intentFilter.addAction(value); 120 result = input.success(null); 121 } 122 break; 123 } 124 case "category": { 125 String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name"); 126 if (value == null) { 127 result = input.error("No value supplied for <android:name>"); 128 } else if (value.isEmpty()) { 129 intentFilter.addCategory(value); 130 // Prior to R, this was not a failure 131 result = input.deferError("No value supplied for <android:name>", 132 ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY); 133 } else { 134 intentFilter.addCategory(value); 135 result = input.success(null); 136 } 137 break; 138 } 139 case "data": 140 result = parseData(intentInfo, res, parser, allowGlobs, input); 141 break; 142 case "uri-relative-filter-group": 143 if (Flags.relativeReferenceIntentFilters()) { 144 result = parseRelRefGroup(intentInfo, pkg, res, parser, allowGlobs, input); 145 break; 146 } 147 default: 148 result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input); 149 break; 150 } 151 152 if (result.isError()) { 153 return input.error(result); 154 } 155 } 156 157 intentInfo.setHasDefault(intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)); 158 159 if (DEBUG) { 160 final StringBuilder cats = new StringBuilder("Intent d="); 161 cats.append(intentInfo.isHasDefault()); 162 cats.append(", cat="); 163 164 final Iterator<String> it = intentFilter.categoriesIterator(); 165 if (it != null) { 166 while (it.hasNext()) { 167 cats.append(' '); 168 cats.append(it.next()); 169 } 170 } 171 Slog.d(TAG, cats.toString()); 172 } 173 174 return input.success(intentInfo); 175 } 176 177 @NonNull 178 @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) parseRelRefGroup(ParsedIntentInfo intentInfo, ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, ParseInput input)179 private static ParseResult<ParsedIntentInfo> parseRelRefGroup(ParsedIntentInfo intentInfo, 180 ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, 181 ParseInput input) throws XmlPullParserException, IOException { 182 IntentFilter intentFilter = intentInfo.getIntentFilter(); 183 TypedArray sa = res.obtainAttributes(parser, 184 R.styleable.AndroidManifestUriRelativeFilterGroup); 185 UriRelativeFilterGroup group; 186 try { 187 int action = UriRelativeFilterGroup.ACTION_ALLOW; 188 if (!sa.getBoolean(R.styleable.AndroidManifestUriRelativeFilterGroup_allow, true)) { 189 action = UriRelativeFilterGroup.ACTION_BLOCK; 190 } 191 group = new UriRelativeFilterGroup(action); 192 } finally { 193 sa.recycle(); 194 } 195 final int depth = parser.getDepth(); 196 int type; 197 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 198 && (type != XmlPullParser.END_TAG 199 || parser.getDepth() > depth)) { 200 if (type != XmlPullParser.START_TAG) { 201 continue; 202 } 203 if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) { 204 continue; 205 } 206 207 final ParseResult result; 208 String nodeName = parser.getName(); 209 switch (nodeName) { 210 case "data": 211 result = parseRelRefGroupData(group, res, parser, allowGlobs, input); 212 break; 213 default: 214 result = ParsingUtils.unknownTag("<uri-relative-filter-group>", 215 pkg, parser, input); 216 break; 217 } 218 219 if (result.isError()) { 220 return input.error(result); 221 } 222 } 223 224 if (group.getUriRelativeFilters().size() > 0) { 225 intentFilter.addUriRelativeFilterGroup(group); 226 } 227 return input.success(null); 228 } 229 230 @NonNull 231 @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) parseRelRefGroupData(UriRelativeFilterGroup group, Resources res, XmlResourceParser parser, boolean allowGlobs, ParseInput input)232 private static ParseResult<ParsedIntentInfo> parseRelRefGroupData(UriRelativeFilterGroup group, 233 Resources res, XmlResourceParser parser, boolean allowGlobs, ParseInput input) { 234 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestData); 235 try { 236 String str = sa.getNonConfigurationString( 237 R.styleable.AndroidManifestData_path, 0); 238 if (str != null) { 239 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, 240 PatternMatcher.PATTERN_LITERAL, str)); 241 } 242 243 str = sa.getNonConfigurationString( 244 R.styleable.AndroidManifestData_pathPrefix, 0); 245 if (str != null) { 246 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, 247 PatternMatcher.PATTERN_PREFIX, str)); 248 } 249 250 str = sa.getNonConfigurationString( 251 R.styleable.AndroidManifestData_pathPattern, 0); 252 if (str != null) { 253 if (!allowGlobs) { 254 return input.error( 255 "pathPattern not allowed here; path must be literal"); 256 } 257 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, 258 PatternMatcher.PATTERN_SIMPLE_GLOB, str)); 259 } 260 261 str = sa.getNonConfigurationString( 262 R.styleable.AndroidManifestData_pathAdvancedPattern, 0); 263 if (str != null) { 264 if (!allowGlobs) { 265 return input.error( 266 "pathAdvancedPattern not allowed here; path must be literal"); 267 } 268 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, 269 PatternMatcher.PATTERN_ADVANCED_GLOB, str)); 270 } 271 272 str = sa.getNonConfigurationString( 273 R.styleable.AndroidManifestData_pathSuffix, 0); 274 if (str != null) { 275 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, 276 PatternMatcher.PATTERN_SUFFIX, str)); 277 } 278 279 str = sa.getNonConfigurationString( 280 R.styleable.AndroidManifestData_fragment, 0); 281 if (str != null) { 282 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, 283 PatternMatcher.PATTERN_LITERAL, str)); 284 } 285 286 str = sa.getNonConfigurationString( 287 R.styleable.AndroidManifestData_fragmentPrefix, 0); 288 if (str != null) { 289 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, 290 PatternMatcher.PATTERN_PREFIX, str)); 291 } 292 293 str = sa.getNonConfigurationString( 294 R.styleable.AndroidManifestData_fragmentPattern, 0); 295 if (str != null) { 296 if (!allowGlobs) { 297 return input.error( 298 "fragmentPattern not allowed here; fragment must be literal"); 299 } 300 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, 301 PatternMatcher.PATTERN_SIMPLE_GLOB, str)); 302 } 303 304 str = sa.getNonConfigurationString( 305 R.styleable.AndroidManifestData_fragmentAdvancedPattern, 0); 306 if (str != null) { 307 if (!allowGlobs) { 308 return input.error( 309 "fragmentAdvancedPattern not allowed here; fragment must be literal"); 310 } 311 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, 312 PatternMatcher.PATTERN_ADVANCED_GLOB, str)); 313 } 314 315 str = sa.getNonConfigurationString( 316 R.styleable.AndroidManifestData_fragmentSuffix, 0); 317 if (str != null) { 318 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, 319 PatternMatcher.PATTERN_SUFFIX, str)); 320 } 321 322 str = sa.getNonConfigurationString( 323 R.styleable.AndroidManifestData_query, 0); 324 if (str != null) { 325 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, 326 PatternMatcher.PATTERN_LITERAL, str)); 327 } 328 329 str = sa.getNonConfigurationString( 330 R.styleable.AndroidManifestData_queryPrefix, 0); 331 if (str != null) { 332 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, 333 PatternMatcher.PATTERN_PREFIX, str)); 334 } 335 336 str = sa.getNonConfigurationString( 337 R.styleable.AndroidManifestData_queryPattern, 0); 338 if (str != null) { 339 if (!allowGlobs) { 340 return input.error( 341 "queryPattern not allowed here; query must be literal"); 342 } 343 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, 344 PatternMatcher.PATTERN_SIMPLE_GLOB, str)); 345 } 346 347 str = sa.getNonConfigurationString( 348 R.styleable.AndroidManifestData_queryAdvancedPattern, 0); 349 if (str != null) { 350 if (!allowGlobs) { 351 return input.error( 352 "queryAdvancedPattern not allowed here; query must be literal"); 353 } 354 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, 355 PatternMatcher.PATTERN_ADVANCED_GLOB, str)); 356 } 357 358 str = sa.getNonConfigurationString( 359 R.styleable.AndroidManifestData_querySuffix, 0); 360 if (str != null) { 361 group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, 362 PatternMatcher.PATTERN_SUFFIX, str)); 363 } 364 365 return input.success(null); 366 } finally { 367 sa.recycle(); 368 } 369 } 370 371 @NonNull parseData(ParsedIntentInfo intentInfo, Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input)372 private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo, 373 Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) { 374 IntentFilter intentFilter = intentInfo.getIntentFilter(); 375 TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData); 376 try { 377 String str = sa.getNonConfigurationString( 378 R.styleable.AndroidManifestData_mimeType, 0); 379 if (str != null) { 380 try { 381 intentFilter.addDataType(str); 382 } catch (IntentFilter.MalformedMimeTypeException e) { 383 return input.error(e.toString()); 384 } 385 } 386 387 str = sa.getNonConfigurationString( 388 R.styleable.AndroidManifestData_mimeGroup, 0); 389 if (str != null) { 390 intentFilter.addMimeGroup(str); 391 } 392 393 str = sa.getNonConfigurationString( 394 R.styleable.AndroidManifestData_scheme, 0); 395 if (str != null) { 396 intentFilter.addDataScheme(str); 397 } 398 399 str = sa.getNonConfigurationString( 400 R.styleable.AndroidManifestData_ssp, 0); 401 if (str != null) { 402 intentFilter.addDataSchemeSpecificPart(str, 403 PatternMatcher.PATTERN_LITERAL); 404 } 405 406 str = sa.getNonConfigurationString( 407 R.styleable.AndroidManifestData_sspPrefix, 0); 408 if (str != null) { 409 intentFilter.addDataSchemeSpecificPart(str, 410 PatternMatcher.PATTERN_PREFIX); 411 } 412 413 str = sa.getNonConfigurationString( 414 R.styleable.AndroidManifestData_sspPattern, 0); 415 if (str != null) { 416 if (!allowGlobs) { 417 return input.error( 418 "sspPattern not allowed here; ssp must be literal"); 419 } 420 intentFilter.addDataSchemeSpecificPart(str, 421 PatternMatcher.PATTERN_SIMPLE_GLOB); 422 } 423 424 str = sa.getNonConfigurationString( 425 R.styleable.AndroidManifestData_sspAdvancedPattern, 0); 426 if (str != null) { 427 if (!allowGlobs) { 428 return input.error( 429 "sspAdvancedPattern not allowed here; ssp must be literal"); 430 } 431 intentFilter.addDataSchemeSpecificPart(str, 432 PatternMatcher.PATTERN_ADVANCED_GLOB); 433 } 434 435 str = sa.getNonConfigurationString( 436 R.styleable.AndroidManifestData_sspSuffix, 0); 437 if (str != null) { 438 intentFilter.addDataSchemeSpecificPart(str, 439 PatternMatcher.PATTERN_SUFFIX); 440 } 441 442 443 String host = sa.getNonConfigurationString( 444 R.styleable.AndroidManifestData_host, 0); 445 String port = sa.getNonConfigurationString( 446 R.styleable.AndroidManifestData_port, 0); 447 if (host != null) { 448 intentFilter.addDataAuthority(host, port); 449 } 450 451 str = sa.getNonConfigurationString( 452 R.styleable.AndroidManifestData_path, 0); 453 if (str != null) { 454 intentFilter.addDataPath(str, PatternMatcher.PATTERN_LITERAL); 455 } 456 457 str = sa.getNonConfigurationString( 458 R.styleable.AndroidManifestData_pathPrefix, 0); 459 if (str != null) { 460 intentFilter.addDataPath(str, PatternMatcher.PATTERN_PREFIX); 461 } 462 463 str = sa.getNonConfigurationString( 464 R.styleable.AndroidManifestData_pathPattern, 0); 465 if (str != null) { 466 if (!allowGlobs) { 467 return input.error( 468 "pathPattern not allowed here; path must be literal"); 469 } 470 intentFilter.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); 471 } 472 473 str = sa.getNonConfigurationString( 474 R.styleable.AndroidManifestData_pathAdvancedPattern, 0); 475 if (str != null) { 476 if (!allowGlobs) { 477 return input.error( 478 "pathAdvancedPattern not allowed here; path must be literal"); 479 } 480 intentFilter.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB); 481 } 482 483 str = sa.getNonConfigurationString( 484 R.styleable.AndroidManifestData_pathSuffix, 0); 485 if (str != null) { 486 intentFilter.addDataPath(str, PatternMatcher.PATTERN_SUFFIX); 487 } 488 489 490 return input.success(null); 491 } finally { 492 sa.recycle(); 493 } 494 } 495 } 496