• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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