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 android.content.res; 18 19 import android.annotation.NonNull; 20 import android.annotation.StyleableRes; 21 import android.ravenwood.annotation.RavenwoodKeepWholeClass; 22 23 import com.android.internal.R; 24 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlPullParserException; 27 28 import java.util.ArrayDeque; 29 30 /** 31 * Validates manifest files by ensuring that tag counts and the length of string attributes are 32 * restricted. 33 * 34 * {@hide} 35 */ 36 @RavenwoodKeepWholeClass 37 public class Validator { 38 39 private final ArrayDeque<Element> mElements = new ArrayDeque<>(); 40 cleanUp()41 private void cleanUp() { 42 while (!mElements.isEmpty()) { 43 mElements.pop().recycle(); 44 } 45 } 46 47 /** 48 * Validates the elements and it's attributes as the XmlPullParser traverses the xml. 49 */ validate(@onNull XmlPullParser parser)50 public void validate(@NonNull XmlPullParser parser) throws XmlPullParserException { 51 int eventType = parser.getEventType(); 52 int depth = parser.getDepth(); 53 // The mElement size should equal to the parser depth-1 when the parser eventType is 54 // START_TAG. If depth - mElement.size() is larger than 1 then that means 55 // validation for the previous element was skipped so we should skip validation for all 56 // descendant elements as well 57 if (depth > mElements.size() + 1) { 58 return; 59 } 60 if (eventType == XmlPullParser.START_TAG) { 61 String tag = parser.getName(); 62 if (Element.shouldValidate(tag)) { 63 Element element = Element.obtain(tag); 64 Element parent = mElements.peek(); 65 if (parent != null && parent.hasChild(tag)) { 66 try { 67 parent.seen(element); 68 } catch (SecurityException e) { 69 cleanUp(); 70 throw e; 71 } 72 } 73 mElements.push(element); 74 } 75 } else if (eventType == XmlPullParser.END_TAG && depth == mElements.size()) { 76 mElements.pop().recycle(); 77 } else if (eventType == XmlPullParser.END_DOCUMENT) { 78 cleanUp(); 79 } 80 } 81 82 /** 83 * Validates the resource string of a manifest tag attribute. 84 */ validateResStrAttr(@onNull XmlPullParser parser, @StyleableRes int index, CharSequence stringValue)85 public void validateResStrAttr(@NonNull XmlPullParser parser, @StyleableRes int index, 86 CharSequence stringValue) { 87 if (parser.getDepth() > mElements.size()) { 88 return; 89 } 90 mElements.peek().validateResStrAttr(index, stringValue); 91 if (index == R.styleable.AndroidManifestMetaData_value) { 92 validateComponentMetadata(stringValue.toString()); 93 } 94 } 95 96 /** 97 * Validates the string of a manifest tag attribute by name. 98 */ validateStrAttr(@onNull XmlPullParser parser, String attrName, String attrValue)99 public void validateStrAttr(@NonNull XmlPullParser parser, String attrName, String attrValue) { 100 if (parser.getDepth() > mElements.size()) { 101 return; 102 } 103 mElements.peek().validateStrAttr(attrName, attrValue); 104 if (attrName.equals(Element.TAG_ATTR_VALUE)) { 105 validateComponentMetadata(attrValue); 106 } 107 } 108 validateComponentMetadata(String attrValue)109 private void validateComponentMetadata(String attrValue) { 110 Element element = mElements.peek(); 111 // Meta-data values are evaluated on the parent element which is the next element in the 112 // mElements stack after the meta-data element. The top of the stack is always the current 113 // element being validated so check that the top element is meta-data. 114 if (element.mTag.equals(Element.TAG_META_DATA) && mElements.size() > 1) { 115 element = mElements.pop(); 116 mElements.peek().validateComponentMetadata(attrValue); 117 mElements.push(element); 118 } 119 } 120 } 121