• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 static android.content.res.Resources.ID_NULL;
20 import static android.system.OsConstants.EINVAL;
21 
22 import android.annotation.AnyRes;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.os.Build;
27 import android.ravenwood.annotation.RavenwoodClassLoadHook;
28 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
29 import android.util.TypedValue;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.internal.pm.pkg.component.AconfigFlags;
33 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
34 import com.android.internal.util.XmlUtils;
35 
36 import dalvik.annotation.optimization.CriticalNative;
37 import dalvik.annotation.optimization.FastNative;
38 
39 import org.xmlpull.v1.XmlPullParserException;
40 
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.Reader;
44 
45 /**
46  * Wrapper around a compiled XML file.
47  *
48  * {@hide}
49  */
50 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
51 @RavenwoodKeepWholeClass
52 @RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
53 public final class XmlBlock implements AutoCloseable {
54     private static final boolean DEBUG=false;
55     public static final String ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android";
56 
57     @UnsupportedAppUsage
XmlBlock(byte[] data)58     public XmlBlock(byte[] data) {
59         mAssets = null;
60         mNative = nativeCreate(data, 0, data.length);
61         mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
62         mUsesFeatureFlags = true;
63     }
64 
XmlBlock(byte[] data, int offset, int size)65     public XmlBlock(byte[] data, int offset, int size) {
66         mAssets = null;
67         mNative = nativeCreate(data, offset, size);
68         mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
69         mUsesFeatureFlags = true;
70     }
71 
72     @Override
close()73     public void close() {
74         synchronized (this) {
75             if (mOpen) {
76                 mOpen = false;
77                 decOpenCountLocked();
78             }
79         }
80     }
81 
decOpenCountLocked()82     private void decOpenCountLocked() {
83         mOpenCount--;
84         if (mOpenCount == 0) {
85             mStrings.close();
86             nativeDestroy(mNative);
87             mNative = 0;
88             if (mAssets != null) {
89                 mAssets.xmlBlockGone(hashCode());
90             }
91         }
92     }
93 
94     @UnsupportedAppUsage
newParser()95     public XmlResourceParser newParser() {
96         return newParser(ID_NULL);
97     }
98 
newParser(@nyRes int resId)99     public XmlResourceParser newParser(@AnyRes int resId) {
100         synchronized (this) {
101             if (mNative != 0) {
102                 return new Parser(nativeCreateParseState(mNative, resId), this);
103             }
104             return null;
105         }
106     }
107 
108     /**
109      * Returns a XmlResourceParser that validates the xml using the given validator.
110      */
newParser(@nyRes int resId, Validator validator)111     public XmlResourceParser newParser(@AnyRes int resId, Validator validator) {
112         synchronized (this) {
113             if (mNative != 0) {
114                 return new Parser(nativeCreateParseState(mNative, resId), this, validator);
115             }
116             return null;
117         }
118     }
119 
120     /**
121      * Reference Error.h UNEXPECTED_NULL
122      */
123     private static final int ERROR_NULL_DOCUMENT = Integer.MIN_VALUE + 8;
124     /**
125      * The reason not to ResXMLParser::BAD_DOCUMENT which is -1 is that other places use the same
126      * value. Reference Error.h BAD_VALUE = -EINVAL
127      */
128     private static final int ERROR_BAD_DOCUMENT = -EINVAL;
129 
130     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
131     public final class Parser implements XmlResourceParser {
132         Validator mValidator;
133 
Parser(long parseState, XmlBlock block)134         Parser(long parseState, XmlBlock block) {
135             mParseState = parseState;
136             mBlock = block;
137             block.mOpenCount++;
138         }
139 
Parser(long parseState, XmlBlock block, Validator validator)140         Parser(long parseState, XmlBlock block, Validator validator) {
141             this(parseState, block);
142             mValidator = validator;
143         }
144 
145         @AnyRes
getSourceResId()146         public int getSourceResId() {
147             return nativeGetSourceResId(mParseState);
148         }
149 
setFeature(String name, boolean state)150         public void setFeature(String name, boolean state) throws XmlPullParserException {
151             if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
152                 return;
153             }
154             if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
155                 return;
156             }
157             throw new XmlPullParserException("Unsupported feature: " + name);
158         }
getFeature(String name)159         public boolean getFeature(String name) {
160             if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
161                 return true;
162             }
163             if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
164                 return true;
165             }
166             return false;
167         }
setProperty(String name, Object value)168         public void setProperty(String name, Object value) throws XmlPullParserException {
169             throw new XmlPullParserException("setProperty() not supported");
170         }
getProperty(String name)171         public Object getProperty(String name) {
172             return null;
173         }
setInput(Reader in)174         public void setInput(Reader in) throws XmlPullParserException {
175             throw new XmlPullParserException("setInput() not supported");
176         }
setInput(InputStream inputStream, String inputEncoding)177         public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
178             throw new XmlPullParserException("setInput() not supported");
179         }
defineEntityReplacementText(String entityName, String replacementText)180         public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
181             throw new XmlPullParserException("defineEntityReplacementText() not supported");
182         }
getNamespacePrefix(int pos)183         public String getNamespacePrefix(int pos) throws XmlPullParserException {
184             throw new XmlPullParserException("getNamespacePrefix() not supported");
185         }
getInputEncoding()186         public String getInputEncoding() {
187             return null;
188         }
getNamespace(String prefix)189         public String getNamespace(String prefix) {
190             throw new RuntimeException("getNamespace() not supported");
191         }
getNamespaceCount(int depth)192         public int getNamespaceCount(int depth) throws XmlPullParserException {
193             throw new XmlPullParserException("getNamespaceCount() not supported");
194         }
getPositionDescription()195         public String getPositionDescription() {
196             return "Binary XML file line #" + getLineNumber();
197         }
getNamespaceUri(int pos)198         public String getNamespaceUri(int pos) throws XmlPullParserException {
199             throw new XmlPullParserException("getNamespaceUri() not supported");
200         }
getColumnNumber()201         public int getColumnNumber() {
202             return -1;
203         }
getDepth()204         public int getDepth() {
205             return mDepth;
206         }
207         @Nullable
getText()208         public String getText() {
209             int id = nativeGetText(mParseState);
210             return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
211         }
getLineNumber()212         public int getLineNumber() {
213             final int lineNumber = nativeGetLineNumber(mParseState);
214             if (lineNumber == ERROR_NULL_DOCUMENT) {
215                 throw new NullPointerException("Null document");
216             }
217             return lineNumber;
218         }
getEventType()219         public int getEventType() throws XmlPullParserException {
220             return mEventType;
221         }
isWhitespace()222         public boolean isWhitespace() throws XmlPullParserException {
223             // whitespace was stripped by aapt.
224             return false;
225         }
getPrefix()226         public String getPrefix() {
227             throw new RuntimeException("getPrefix not supported");
228         }
getTextCharacters(int[] holderForStartAndLength)229         public char[] getTextCharacters(int[] holderForStartAndLength) {
230             String txt = getText();
231             char[] chars = null;
232             if (txt != null) {
233                 holderForStartAndLength[0] = 0;
234                 holderForStartAndLength[1] = txt.length();
235                 chars = new char[txt.length()];
236                 txt.getChars(0, txt.length(), chars, 0);
237             }
238             return chars;
239         }
240         @Nullable
getNamespace()241         public String getNamespace() {
242             int id = nativeGetNamespace(mParseState);
243             return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : "";
244         }
245         @Nullable
getName()246         public String getName() {
247             int id = nativeGetName(mParseState);
248             return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
249         }
250         @NonNull
getAttributeNamespace(int index)251         public String getAttributeNamespace(int index) {
252             final int id = nativeGetAttributeNamespace(mParseState, index);
253             if (id == ERROR_NULL_DOCUMENT) {
254                 throw new NullPointerException("Null document");
255             }
256             if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id);
257             if (id >= 0) return getSequenceString(mStrings.getSequence(id));
258             else if (id == -1) return "";
259             throw new IndexOutOfBoundsException(String.valueOf(index));
260         }
261         @NonNull
getAttributeName(int index)262         public String getAttributeName(int index) {
263             final int id = nativeGetAttributeName(mParseState, index);
264             if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id);
265             if (id == ERROR_NULL_DOCUMENT) {
266                 throw new NullPointerException("Null document");
267             }
268             if (id >= 0) return getSequenceString(mStrings.getSequence(id));
269             throw new IndexOutOfBoundsException(String.valueOf(index));
270         }
getAttributePrefix(int index)271         public String getAttributePrefix(int index) {
272             throw new RuntimeException("getAttributePrefix not supported");
273         }
isEmptyElementTag()274         public boolean isEmptyElementTag() throws XmlPullParserException {
275             // XXX Need to detect this.
276             return false;
277         }
getAttributeCount()278         public int getAttributeCount() {
279             if (mEventType == START_TAG) {
280                 final int count = nativeGetAttributeCount(mParseState);
281                 if (count == ERROR_NULL_DOCUMENT) {
282                     throw new NullPointerException("Null document");
283                 }
284                 return count;
285             } else {
286                 return -1;
287             }
288         }
289         @NonNull
getAttributeValue(int index)290         public String getAttributeValue(int index) {
291             final int id = nativeGetAttributeStringValue(mParseState, index);
292             if (id == ERROR_NULL_DOCUMENT) {
293                 throw new NullPointerException("Null document");
294             }
295             if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id);
296             if (id >= 0) return getSequenceString(mStrings.getSequence(id));
297 
298             // May be some other type...  check and try to convert if so.
299             final int t = nativeGetAttributeDataType(mParseState, index);
300             if (t == ERROR_NULL_DOCUMENT) {
301                 throw new NullPointerException("Null document");
302             }
303             if (t == TypedValue.TYPE_NULL) {
304                 throw new IndexOutOfBoundsException(String.valueOf(index));
305             }
306 
307             final int v = nativeGetAttributeData(mParseState, index);
308             if (v == ERROR_NULL_DOCUMENT) {
309                 throw new NullPointerException("Null document");
310             }
311             return TypedValue.coerceToString(t, v);
312         }
getAttributeType(int index)313         public String getAttributeType(int index) {
314             return "CDATA";
315         }
isAttributeDefault(int index)316         public boolean isAttributeDefault(int index) {
317             return false;
318         }
nextToken()319         public int nextToken() throws XmlPullParserException,IOException {
320             return next();
321         }
getAttributeValue(String namespace, String name)322         public String getAttributeValue(String namespace, String name) {
323             int idx = nativeGetAttributeIndex(mParseState, namespace, name);
324             if (idx >= 0) {
325                 if (DEBUG) System.out.println("getAttributeName of "
326                         + namespace + ":" + name + " index = " + idx);
327                 if (DEBUG) System.out.println(
328                         "Namespace=" + getAttributeNamespace(idx)
329                         + "Name=" + getAttributeName(idx)
330                         + ", Value=" + getAttributeValue(idx));
331                 String value = getAttributeValue(idx);
332                 if (mValidator != null) {
333                     mValidator.validateStrAttr(this, name, value);
334                 }
335                 return value;
336             }
337             return null;
338         }
next()339         public int next() throws XmlPullParserException,IOException {
340             if (!mStarted) {
341                 mStarted = true;
342                 return START_DOCUMENT;
343             }
344             if (mParseState == 0) {
345                 return END_DOCUMENT;
346             }
347             int ev = nativeNext(mParseState);
348             if (ev == ERROR_BAD_DOCUMENT) {
349                 throw new XmlPullParserException("Corrupt XML binary file");
350             }
351 
352             if (useLayoutReadwrite() && mUsesFeatureFlags && ev == START_TAG) {
353                 AconfigFlags flags = ParsingPackageUtils.getAconfigFlags();
354                 if (flags.skipCurrentElement(/* pkg= */ null, this)) {
355                     int depth = 1;
356                     while (depth > 0) {
357                         int ev2 = nativeNext(mParseState);
358                         if (ev2 == ERROR_BAD_DOCUMENT) {
359                             throw new XmlPullParserException("Corrupt XML binary file");
360                         } else if (ev2 == START_TAG) {
361                             depth++;
362                         } else if (ev2 == END_TAG) {
363                             depth--;
364                         }
365                     }
366                     return next();
367                 }
368             }
369             if (mDecNextDepth) {
370                 mDepth--;
371                 mDecNextDepth = false;
372             }
373             switch (ev) {
374             case START_TAG:
375                 mDepth++;
376                 break;
377             case END_TAG:
378                 mDecNextDepth = true;
379                 break;
380             }
381             mEventType = ev;
382             if (mValidator != null) {
383                 mValidator.validate(this);
384             }
385             if (ev == END_DOCUMENT) {
386                 // Automatically close the parse when we reach the end of
387                 // a document, since the standard XmlPullParser interface
388                 // doesn't have such an API so most clients will leave us
389                 // dangling.
390                 close();
391             }
392             return ev;
393         }
394 
395         // Until ravenwood supports AconfigFlags, we just don't do layoutReadwriteFlags().
396         @android.ravenwood.annotation.RavenwoodReplace(
397                 bug = 396458006, blockedBy = AconfigFlags.class)
useLayoutReadwrite()398         private static boolean useLayoutReadwrite() {
399             return Flags.layoutReadwriteFlags();
400         }
401 
useLayoutReadwrite$ravenwood()402         private static boolean useLayoutReadwrite$ravenwood() {
403             return false;
404         }
405 
require(int type, String namespace, String name)406         public void require(int type, String namespace, String name) throws XmlPullParserException,IOException {
407             if (type != getEventType()
408                 || (namespace != null && !namespace.equals( getNamespace () ) )
409                 || (name != null && !name.equals( getName() ) ) )
410                 throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
411         }
nextText()412         public String nextText() throws XmlPullParserException,IOException {
413             if(getEventType() != START_TAG) {
414                throw new XmlPullParserException(
415                  getPositionDescription()
416                  + ": parser must be on START_TAG to read next text", this, null);
417             }
418             int eventType = next();
419             if(eventType == TEXT) {
420                String result = getText();
421                eventType = next();
422                if(eventType != END_TAG) {
423                  throw new XmlPullParserException(
424                     getPositionDescription()
425                     + ": event TEXT it must be immediately followed by END_TAG", this, null);
426                 }
427                 return result;
428             } else if(eventType == END_TAG) {
429                return "";
430             } else {
431                throw new XmlPullParserException(
432                  getPositionDescription()
433                  + ": parser must be on START_TAG or TEXT to read text", this, null);
434             }
435         }
nextTag()436         public int nextTag() throws XmlPullParserException,IOException {
437             int eventType = next();
438             if(eventType == TEXT && isWhitespace()) {   // skip whitespace
439                eventType = next();
440             }
441             if (eventType != START_TAG && eventType != END_TAG) {
442                throw new XmlPullParserException(
443                    getPositionDescription()
444                    + ": expected start or end tag", this, null);
445             }
446             return eventType;
447         }
448 
getAttributeNameResource(int index)449         public int getAttributeNameResource(int index) {
450             final int resourceNameId = nativeGetAttributeResource(mParseState, index);
451             if (resourceNameId == ERROR_NULL_DOCUMENT) {
452                 throw new NullPointerException("Null document");
453             }
454             return resourceNameId;
455         }
456 
getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue)457         public int getAttributeListValue(String namespace, String attribute,
458                 String[] options, int defaultValue) {
459             int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
460             if (idx >= 0) {
461                 return getAttributeListValue(idx, options, defaultValue);
462             }
463             return defaultValue;
464         }
getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue)465         public boolean getAttributeBooleanValue(String namespace, String attribute,
466                 boolean defaultValue) {
467             int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
468             if (idx >= 0) {
469                 return getAttributeBooleanValue(idx, defaultValue);
470             }
471             return defaultValue;
472         }
getAttributeResourceValue(String namespace, String attribute, int defaultValue)473         public int getAttributeResourceValue(String namespace, String attribute,
474                 int defaultValue) {
475             int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
476             if (idx >= 0) {
477                 return getAttributeResourceValue(idx, defaultValue);
478             }
479             return defaultValue;
480         }
getAttributeIntValue(String namespace, String attribute, int defaultValue)481         public int getAttributeIntValue(String namespace, String attribute,
482                 int defaultValue) {
483             int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
484             if (idx >= 0) {
485                 return getAttributeIntValue(idx, defaultValue);
486             }
487             return defaultValue;
488         }
getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue)489         public int getAttributeUnsignedIntValue(String namespace, String attribute,
490                                                 int defaultValue)
491         {
492             int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
493             if (idx >= 0) {
494                 return getAttributeUnsignedIntValue(idx, defaultValue);
495             }
496             return defaultValue;
497         }
getAttributeFloatValue(String namespace, String attribute, float defaultValue)498         public float getAttributeFloatValue(String namespace, String attribute,
499                 float defaultValue) {
500             int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
501             if (idx >= 0) {
502                 return getAttributeFloatValue(idx, defaultValue);
503             }
504             return defaultValue;
505         }
506 
getAttributeListValue(int idx, String[] options, int defaultValue)507         public int getAttributeListValue(int idx,
508                 String[] options, int defaultValue) {
509             final int t = nativeGetAttributeDataType(mParseState, idx);
510             if (t == ERROR_NULL_DOCUMENT) {
511                 throw new NullPointerException("Null document");
512             }
513             final int v = nativeGetAttributeData(mParseState, idx);
514             if (v == ERROR_NULL_DOCUMENT) {
515                 throw new NullPointerException("Null document");
516             }
517             if (t == TypedValue.TYPE_STRING) {
518                 return XmlUtils.convertValueToList(
519                     mStrings.getSequence(v), options, defaultValue);
520             }
521             return v;
522         }
getAttributeBooleanValue(int idx, boolean defaultValue)523         public boolean getAttributeBooleanValue(int idx,
524                 boolean defaultValue) {
525             final int t = nativeGetAttributeDataType(mParseState, idx);
526             if (t == ERROR_NULL_DOCUMENT) {
527                 throw new NullPointerException("Null document");
528             }
529             // Note: don't attempt to convert any other types, because
530             // we want to count on aapt doing the conversion for us.
531             if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) {
532                 final int v = nativeGetAttributeData(mParseState, idx);
533                 if (v == ERROR_NULL_DOCUMENT) {
534                     throw new NullPointerException("Null document");
535                 }
536                 return v != 0;
537             }
538             return defaultValue;
539         }
getAttributeResourceValue(int idx, int defaultValue)540         public int getAttributeResourceValue(int idx, int defaultValue) {
541             final int t = nativeGetAttributeDataType(mParseState, idx);
542             if (t == ERROR_NULL_DOCUMENT) {
543                 throw new NullPointerException("Null document");
544             }
545             // Note: don't attempt to convert any other types, because
546             // we want to count on aapt doing the conversion for us.
547             if (t == TypedValue.TYPE_REFERENCE) {
548                 final int v = nativeGetAttributeData(mParseState, idx);
549                 if (v == ERROR_NULL_DOCUMENT) {
550                     throw new NullPointerException("Null document");
551                 }
552                 return v;
553             }
554             return defaultValue;
555         }
getAttributeIntValue(int idx, int defaultValue)556         public int getAttributeIntValue(int idx, int defaultValue) {
557             final int t = nativeGetAttributeDataType(mParseState, idx);
558             if (t == ERROR_NULL_DOCUMENT) {
559                 throw new NullPointerException("Null document");
560             }
561             // Note: don't attempt to convert any other types, because
562             // we want to count on aapt doing the conversion for us.
563             if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) {
564                 final int v = nativeGetAttributeData(mParseState, idx);
565                 if (v == ERROR_NULL_DOCUMENT) {
566                     throw new NullPointerException("Null document");
567                 }
568                 return v;
569             }
570             return defaultValue;
571         }
getAttributeUnsignedIntValue(int idx, int defaultValue)572         public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
573             int t = nativeGetAttributeDataType(mParseState, idx);
574             if (t == ERROR_NULL_DOCUMENT) {
575                 throw new NullPointerException("Null document");
576             }
577             // Note: don't attempt to convert any other types, because
578             // we want to count on aapt doing the conversion for us.
579             if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) {
580                 final int v = nativeGetAttributeData(mParseState, idx);
581                 if (v == ERROR_NULL_DOCUMENT) {
582                     throw new NullPointerException("Null document");
583                 }
584                 return v;
585             }
586             return defaultValue;
587         }
getAttributeFloatValue(int idx, float defaultValue)588         public float getAttributeFloatValue(int idx, float defaultValue) {
589             final int t = nativeGetAttributeDataType(mParseState, idx);
590             if (t == ERROR_NULL_DOCUMENT) {
591                 throw new NullPointerException("Null document");
592             }
593             // Note: don't attempt to convert any other types, because
594             // we want to count on aapt doing the conversion for us.
595             if (t == TypedValue.TYPE_FLOAT) {
596                 final int v = nativeGetAttributeData(mParseState, idx);
597                 if (v == ERROR_NULL_DOCUMENT) {
598                     throw new NullPointerException("Null document");
599                 }
600                 return Float.intBitsToFloat(v);
601             }
602             throw new RuntimeException("not a float!");
603         }
604         @Nullable
getIdAttribute()605         public String getIdAttribute() {
606             final int id = nativeGetIdAttribute(mParseState);
607             if (id == ERROR_NULL_DOCUMENT) {
608                 throw new NullPointerException("Null document");
609             }
610             return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
611         }
612         @Nullable
getClassAttribute()613         public String getClassAttribute() {
614             final int id = nativeGetClassAttribute(mParseState);
615             if (id == ERROR_NULL_DOCUMENT) {
616                 throw new NullPointerException("Null document");
617             }
618             return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
619         }
620 
getIdAttributeResourceValue(int defaultValue)621         public int getIdAttributeResourceValue(int defaultValue) {
622             //todo: create and use native method
623             return getAttributeResourceValue(null, "id", defaultValue);
624         }
625 
getStyleAttribute()626         public int getStyleAttribute() {
627             final int styleAttributeId = nativeGetStyleAttribute(mParseState);
628             if (styleAttributeId == ERROR_NULL_DOCUMENT) {
629                 throw new NullPointerException("Null document");
630             }
631             return styleAttributeId;
632         }
633 
getSequenceString(@ullable CharSequence str)634         private String getSequenceString(@Nullable CharSequence str) {
635             if (str == null) {
636                 // A value of null retrieved from a StringPool indicates that retrieval of the
637                 // string failed due to incremental installation. The presence of all the XmlBlock
638                 // data is verified when it is created, so this exception must not be possible.
639                 throw new IllegalStateException("Retrieving a string from the StringPool of an"
640                         + " XmlBlock should never fail");
641             }
642             return str.toString();
643         }
644 
close()645         public void close() {
646             synchronized (mBlock) {
647                 if (mParseState != 0) {
648                     nativeDestroyParseState(mParseState);
649                     mParseState = 0;
650                     mBlock.decOpenCountLocked();
651                 }
652             }
653         }
654 
finalize()655         protected void finalize() throws Throwable {
656             close();
657         }
658 
659         @Nullable
getPooledString(int id)660         /*package*/ final CharSequence getPooledString(int id) {
661             return mStrings.getSequence(id);
662         }
663 
664         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
665         /*package*/ long mParseState;
666         @UnsupportedAppUsage
667         private final XmlBlock mBlock;
668         private boolean mStarted = false;
669         private boolean mDecNextDepth = false;
670         private int mDepth = 0;
671         private int mEventType = START_DOCUMENT;
672     }
673 
finalize()674     protected void finalize() throws Throwable {
675         close();
676     }
677 
678     /**
679      * Create from an existing xml block native object.  This is
680      * -extremely- dangerous -- only use it if you absolutely know what you
681      *  are doing!  The given native object must exist for the entire lifetime
682      *  of this newly creating XmlBlock.
683      */
XmlBlock(@ullable AssetManager assets, long xmlBlock, boolean usesFeatureFlags)684     XmlBlock(@Nullable AssetManager assets, long xmlBlock, boolean usesFeatureFlags) {
685         mAssets = assets;
686         mNative = xmlBlock;
687         mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
688         mUsesFeatureFlags = usesFeatureFlags;
689     }
690 
691     private @Nullable final AssetManager mAssets;
692     private long mNative;   // final, but gets reset on close
693     /*package*/ final StringBlock mStrings;
694     private boolean mOpen = true;
695     private int mOpenCount = 1;
696 
697     private final boolean mUsesFeatureFlags;
698 
nativeCreate(byte[] data, int offset, int size)699     private static final native long nativeCreate(byte[] data,
700                                                  int offset,
701                                                  int size);
nativeGetStringBlock(long obj)702     private static final native long nativeGetStringBlock(long obj);
nativeCreateParseState(long obj, int resId)703     private static final native long nativeCreateParseState(long obj, int resId);
nativeDestroyParseState(long state)704     private static final native void nativeDestroyParseState(long state);
nativeDestroy(long obj)705     private static final native void nativeDestroy(long obj);
706 
707     // ----------- @FastNative ------------------
708 
709     @FastNative
nativeGetAttributeIndex( long state, String namespace, String name)710     private static native int nativeGetAttributeIndex(
711             long state, String namespace, String name);
712 
713     // ----------- @CriticalNative ------------------
714     @CriticalNative
nativeNext(long state)715     /*package*/ static final native int nativeNext(long state);
716 
717     @CriticalNative
nativeGetNamespace(long state)718     private static final native int nativeGetNamespace(long state);
719 
720     @CriticalNative
nativeGetName(long state)721     /*package*/ static final native int nativeGetName(long state);
722 
723     @CriticalNative
nativeGetText(long state)724     private static final native int nativeGetText(long state);
725 
726     @CriticalNative
nativeGetLineNumber(long state)727     private static final native int nativeGetLineNumber(long state);
728 
729     @CriticalNative
nativeGetAttributeCount(long state)730     private static final native int nativeGetAttributeCount(long state);
731 
732     @CriticalNative
nativeGetAttributeNamespace(long state, int idx)733     private static final native int nativeGetAttributeNamespace(long state, int idx);
734 
735     @CriticalNative
nativeGetAttributeName(long state, int idx)736     private static final native int nativeGetAttributeName(long state, int idx);
737 
738     @CriticalNative
nativeGetAttributeResource(long state, int idx)739     private static final native int nativeGetAttributeResource(long state, int idx);
740 
741     @CriticalNative
nativeGetAttributeDataType(long state, int idx)742     private static final native int nativeGetAttributeDataType(long state, int idx);
743 
744     @CriticalNative
nativeGetAttributeData(long state, int idx)745     private static final native int nativeGetAttributeData(long state, int idx);
746 
747     @CriticalNative
nativeGetAttributeStringValue(long state, int idx)748     private static final native int nativeGetAttributeStringValue(long state, int idx);
749 
750     @CriticalNative
nativeGetIdAttribute(long state)751     private static final native int nativeGetIdAttribute(long state);
752 
753     @CriticalNative
nativeGetClassAttribute(long state)754     private static final native int nativeGetClassAttribute(long state);
755 
756     @CriticalNative
nativeGetStyleAttribute(long state)757     private static final native int nativeGetStyleAttribute(long state);
758 
759     @CriticalNative
nativeGetSourceResId(long state)760     private static final native int nativeGetSourceResId(long state);
761 }
762