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