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 com.android.internal.util;
18 
19 import android.annotation.NonNull;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.graphics.Bitmap;
22 import android.graphics.Bitmap.CompressFormat;
23 import android.graphics.BitmapFactory;
24 import android.net.Uri;
25 import android.text.TextUtils;
26 import android.util.ArrayMap;
27 import android.util.Base64;
28 import android.util.Xml;
29 
30 import com.android.modules.utils.TypedXmlPullParser;
31 import com.android.modules.utils.TypedXmlSerializer;
32 
33 import libcore.util.HexEncoding;
34 
35 import org.xmlpull.v1.XmlPullParser;
36 import org.xmlpull.v1.XmlPullParserException;
37 import org.xmlpull.v1.XmlSerializer;
38 
39 import java.io.ByteArrayOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.OutputStream;
43 import java.net.ProtocolException;
44 import java.nio.charset.StandardCharsets;
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.HashSet;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Set;
52 
53 /** {@hide} */
54 @android.ravenwood.annotation.RavenwoodKeepWholeClass
55 public class XmlUtils {
56     private static final String STRING_ARRAY_SEPARATOR = ":";
57 
58     @SuppressWarnings("AndroidFrameworkEfficientXml")
59     private static class ForcedTypedXmlSerializer extends XmlSerializerWrapper
60             implements TypedXmlSerializer {
ForcedTypedXmlSerializer(XmlSerializer wrapped)61         public ForcedTypedXmlSerializer(XmlSerializer wrapped) {
62             super(wrapped);
63         }
64 
65         @Override
attributeInterned(String namespace, String name, String value)66         public XmlSerializer attributeInterned(String namespace, String name, String value)
67                 throws IOException {
68             return attribute(namespace, name, value);
69         }
70 
71         @Override
attributeBytesHex(String namespace, String name, byte[] value)72         public XmlSerializer attributeBytesHex(String namespace, String name, byte[] value)
73                 throws IOException {
74             return attribute(namespace, name, HexDump.toHexString(value));
75         }
76 
77         @Override
attributeBytesBase64(String namespace, String name, byte[] value)78         public XmlSerializer attributeBytesBase64(String namespace, String name, byte[] value)
79                 throws IOException {
80             return attribute(namespace, name, Base64.encodeToString(value, Base64.NO_WRAP));
81         }
82 
83         @Override
attributeInt(String namespace, String name, int value)84         public XmlSerializer attributeInt(String namespace, String name, int value)
85                 throws IOException {
86             return attribute(namespace, name, Integer.toString(value));
87         }
88 
89         @Override
attributeIntHex(String namespace, String name, int value)90         public XmlSerializer attributeIntHex(String namespace, String name, int value)
91                 throws IOException {
92             return attribute(namespace, name, Integer.toString(value, 16));
93         }
94 
95         @Override
attributeLong(String namespace, String name, long value)96         public XmlSerializer attributeLong(String namespace, String name, long value)
97                 throws IOException {
98             return attribute(namespace, name, Long.toString(value));
99         }
100 
101         @Override
attributeLongHex(String namespace, String name, long value)102         public XmlSerializer attributeLongHex(String namespace, String name, long value)
103                 throws IOException {
104             return attribute(namespace, name, Long.toString(value, 16));
105         }
106 
107         @Override
attributeFloat(String namespace, String name, float value)108         public XmlSerializer attributeFloat(String namespace, String name, float value)
109                 throws IOException {
110             return attribute(namespace, name, Float.toString(value));
111         }
112 
113         @Override
attributeDouble(String namespace, String name, double value)114         public XmlSerializer attributeDouble(String namespace, String name, double value)
115                 throws IOException {
116             return attribute(namespace, name, Double.toString(value));
117         }
118 
119         @Override
attributeBoolean(String namespace, String name, boolean value)120         public XmlSerializer attributeBoolean(String namespace, String name, boolean value)
121                 throws IOException {
122             return attribute(namespace, name, Boolean.toString(value));
123         }
124     }
125 
126     /**
127      * Return a specialization of the given {@link XmlSerializer} which has
128      * explicit methods to support consistent and efficient conversion of
129      * primitive data types.
130      */
makeTyped(@onNull XmlSerializer xml)131     public static @NonNull TypedXmlSerializer makeTyped(@NonNull XmlSerializer xml) {
132         if (xml instanceof TypedXmlSerializer) {
133             return (TypedXmlSerializer) xml;
134         } else {
135             return new ForcedTypedXmlSerializer(xml);
136         }
137     }
138 
139     @SuppressWarnings("AndroidFrameworkEfficientXml")
140     private static class ForcedTypedXmlPullParser extends XmlPullParserWrapper
141             implements TypedXmlPullParser {
ForcedTypedXmlPullParser(XmlPullParser wrapped)142         public ForcedTypedXmlPullParser(XmlPullParser wrapped) {
143             super(wrapped);
144         }
145 
146         @Override
getAttributeBytesHex(int index)147         public byte[] getAttributeBytesHex(int index)
148                 throws XmlPullParserException {
149             try {
150                 return HexDump.hexStringToByteArray(getAttributeValue(index));
151             } catch (Exception e) {
152                 throw new XmlPullParserException(
153                         "Invalid attribute " + getAttributeName(index) + ": " + e);
154             }
155         }
156 
157         @Override
getAttributeBytesBase64(int index)158         public byte[] getAttributeBytesBase64(int index)
159                 throws XmlPullParserException {
160             try {
161                 return Base64.decode(getAttributeValue(index), Base64.NO_WRAP);
162             } catch (Exception e) {
163                 throw new XmlPullParserException(
164                         "Invalid attribute " + getAttributeName(index) + ": " + e);
165             }
166         }
167 
168         @Override
getAttributeInt(int index)169         public int getAttributeInt(int index)
170                 throws XmlPullParserException {
171             try {
172                 return Integer.parseInt(getAttributeValue(index));
173             } catch (Exception e) {
174                 throw new XmlPullParserException(
175                         "Invalid attribute " + getAttributeName(index) + ": " + e);
176             }
177         }
178 
179         @Override
getAttributeIntHex(int index)180         public int getAttributeIntHex(int index)
181                 throws XmlPullParserException {
182             try {
183                 return Integer.parseInt(getAttributeValue(index), 16);
184             } catch (Exception e) {
185                 throw new XmlPullParserException(
186                         "Invalid attribute " + getAttributeName(index) + ": " + e);
187             }
188         }
189 
190         @Override
getAttributeLong(int index)191         public long getAttributeLong(int index)
192                 throws XmlPullParserException {
193             try {
194                 return Long.parseLong(getAttributeValue(index));
195             } catch (Exception e) {
196                 throw new XmlPullParserException(
197                         "Invalid attribute " + getAttributeName(index) + ": " + e);
198             }
199         }
200 
201         @Override
getAttributeLongHex(int index)202         public long getAttributeLongHex(int index)
203                 throws XmlPullParserException {
204             try {
205                 return Long.parseLong(getAttributeValue(index), 16);
206             } catch (Exception e) {
207                 throw new XmlPullParserException(
208                         "Invalid attribute " + getAttributeName(index) + ": " + e);
209             }
210         }
211 
212         @Override
getAttributeFloat(int index)213         public float getAttributeFloat(int index)
214                 throws XmlPullParserException {
215             try {
216                 return Float.parseFloat(getAttributeValue(index));
217             } catch (Exception e) {
218                 throw new XmlPullParserException(
219                         "Invalid attribute " + getAttributeName(index) + ": " + e);
220             }
221         }
222 
223         @Override
getAttributeDouble(int index)224         public double getAttributeDouble(int index)
225                 throws XmlPullParserException {
226             try {
227                 return Double.parseDouble(getAttributeValue(index));
228             } catch (Exception e) {
229                 throw new XmlPullParserException(
230                         "Invalid attribute " + getAttributeName(index) + ": " + e);
231             }
232         }
233 
234         @Override
getAttributeBoolean(int index)235         public boolean getAttributeBoolean(int index)
236                 throws XmlPullParserException {
237             final String value = getAttributeValue(index);
238             if ("true".equalsIgnoreCase(value)) {
239                 return true;
240             } else if ("false".equalsIgnoreCase(value)) {
241                 return false;
242             } else {
243                 throw new XmlPullParserException(
244                         "Invalid attribute " + getAttributeName(index) + ": " + value);
245             }
246         }
247     }
248 
249     /**
250      * Return a specialization of the given {@link XmlPullParser} which has
251      * explicit methods to support consistent and efficient conversion of
252      * primitive data types.
253      */
makeTyped(@onNull XmlPullParser xml)254     public static @NonNull TypedXmlPullParser makeTyped(@NonNull XmlPullParser xml) {
255         if (xml instanceof TypedXmlPullParser) {
256             return (TypedXmlPullParser) xml;
257         } else {
258             return new ForcedTypedXmlPullParser(xml);
259         }
260     }
261 
262     @UnsupportedAppUsage
skipCurrentTag(XmlPullParser parser)263     public static void skipCurrentTag(XmlPullParser parser)
264             throws XmlPullParserException, IOException {
265         int outerDepth = parser.getDepth();
266         int type;
267         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
268                && (type != XmlPullParser.END_TAG
269                        || parser.getDepth() > outerDepth)) {
270         }
271     }
272 
273     public static final int
convertValueToList(CharSequence value, String[] options, int defaultValue)274     convertValueToList(CharSequence value, String[] options, int defaultValue)
275     {
276         if (!TextUtils.isEmpty(value)) {
277             for (int i = 0; i < options.length; i++) {
278                 if (value.equals(options[i]))
279                     return i;
280             }
281         }
282 
283         return defaultValue;
284     }
285 
286     @UnsupportedAppUsage
287     public static final boolean
convertValueToBoolean(CharSequence value, boolean defaultValue)288     convertValueToBoolean(CharSequence value, boolean defaultValue)
289     {
290         boolean result = false;
291 
292         if (TextUtils.isEmpty(value)) {
293             return defaultValue;
294         }
295 
296         if (value.equals("1")
297         ||  value.equals("true")
298         ||  value.equals("TRUE"))
299             result = true;
300 
301         return result;
302     }
303 
304     @UnsupportedAppUsage
305     public static final int
convertValueToInt(CharSequence charSeq, int defaultValue)306     convertValueToInt(CharSequence charSeq, int defaultValue)
307     {
308         if (TextUtils.isEmpty(charSeq)) {
309             return defaultValue;
310         }
311 
312         String nm = charSeq.toString();
313 
314         // XXX This code is copied from Integer.decode() so we don't
315         // have to instantiate an Integer!
316 
317         int value;
318         int sign = 1;
319         int index = 0;
320         int len = nm.length();
321         int base = 10;
322 
323         if ('-' == nm.charAt(0)) {
324             sign = -1;
325             index++;
326         }
327 
328         if ('0' == nm.charAt(index)) {
329             //  Quick check for a zero by itself
330             if (index == (len - 1))
331                 return 0;
332 
333             char    c = nm.charAt(index + 1);
334 
335             if ('x' == c || 'X' == c) {
336                 index += 2;
337                 base = 16;
338             } else {
339                 index++;
340                 base = 8;
341             }
342         }
343         else if ('#' == nm.charAt(index))
344         {
345             index++;
346             base = 16;
347         }
348 
349         return Integer.parseInt(nm.substring(index), base) * sign;
350     }
351 
convertValueToUnsignedInt(String value, int defaultValue)352     public static int convertValueToUnsignedInt(String value, int defaultValue) {
353         if (TextUtils.isEmpty(value)) {
354             return defaultValue;
355         }
356 
357         return parseUnsignedIntAttribute(value);
358     }
359 
parseUnsignedIntAttribute(CharSequence charSeq)360     public static int parseUnsignedIntAttribute(CharSequence charSeq) {
361         String  value = charSeq.toString();
362 
363         long    bits;
364         int     index = 0;
365         int     len = value.length();
366         int     base = 10;
367 
368         if ('0' == value.charAt(index)) {
369             //  Quick check for zero by itself
370             if (index == (len - 1))
371                 return 0;
372 
373             char    c = value.charAt(index + 1);
374 
375             if ('x' == c || 'X' == c) {     //  check for hex
376                 index += 2;
377                 base = 16;
378             } else {                        //  check for octal
379                 index++;
380                 base = 8;
381             }
382         } else if ('#' == value.charAt(index)) {
383             index++;
384             base = 16;
385         }
386 
387         return (int) Long.parseLong(value.substring(index), base);
388     }
389 
390     /**
391      * Flatten a Map into an output stream as XML.  The map can later be
392      * read back with readMapXml().
393      *
394      * @param val The map to be flattened.
395      * @param out Where to write the XML data.
396      *
397      * @see #writeMapXml(Map, String, XmlSerializer)
398      * @see #writeListXml
399      * @see #writeValueXml
400      * @see #readMapXml
401      */
402     @UnsupportedAppUsage
writeMapXml(Map val, OutputStream out)403     public static final void writeMapXml(Map val, OutputStream out)
404             throws XmlPullParserException, java.io.IOException {
405         TypedXmlSerializer serializer = Xml.newFastSerializer();
406         serializer.setOutput(out, StandardCharsets.UTF_8.name());
407         serializer.startDocument(null, true);
408         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
409         writeMapXml(val, null, serializer);
410         serializer.endDocument();
411     }
412 
413     /**
414      * Flatten a List into an output stream as XML.  The list can later be
415      * read back with readListXml().
416      *
417      * @param val The list to be flattened.
418      * @param out Where to write the XML data.
419      *
420      * @see #writeListXml(List, String, XmlSerializer)
421      * @see #writeMapXml
422      * @see #writeValueXml
423      * @see #readListXml
424      */
writeListXml(List val, OutputStream out)425     public static final void writeListXml(List val, OutputStream out)
426     throws XmlPullParserException, java.io.IOException
427     {
428         TypedXmlSerializer serializer = Xml.newFastSerializer();
429         serializer.setOutput(out, StandardCharsets.UTF_8.name());
430         serializer.startDocument(null, true);
431         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
432         writeListXml(val, null, serializer);
433         serializer.endDocument();
434     }
435 
436     /**
437      * Flatten a Map into an XmlSerializer.  The map can later be read back
438      * with readThisMapXml().
439      *
440      * @param val The map to be flattened.
441      * @param name Name attribute to include with this list's tag, or null for
442      *             none.
443      * @param out XmlSerializer to write the map into.
444      *
445      * @see #writeMapXml(Map, OutputStream)
446      * @see #writeListXml
447      * @see #writeValueXml
448      * @see #readMapXml
449      */
writeMapXml(Map val, String name, TypedXmlSerializer out)450     public static final void writeMapXml(Map val, String name, TypedXmlSerializer out)
451             throws XmlPullParserException, java.io.IOException {
452         writeMapXml(val, name, out, null);
453     }
454 
455     /**
456      * Flatten a Map into an XmlSerializer.  The map can later be read back
457      * with readThisMapXml().
458      *
459      * @param val The map to be flattened.
460      * @param name Name attribute to include with this list's tag, or null for
461      *             none.
462      * @param out XmlSerializer to write the map into.
463      * @param callback Method to call when an Object type is not recognized.
464      *
465      * @see #writeMapXml(Map, OutputStream)
466      * @see #writeListXml
467      * @see #writeValueXml
468      * @see #readMapXml
469      *
470      * @hide
471      */
writeMapXml(Map val, String name, TypedXmlSerializer out, WriteMapCallback callback)472     public static final void writeMapXml(Map val, String name, TypedXmlSerializer out,
473             WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
474 
475         if (val == null) {
476             out.startTag(null, "null");
477             out.endTag(null, "null");
478             return;
479         }
480 
481         out.startTag(null, "map");
482         if (name != null) {
483             out.attribute(null, "name", name);
484         }
485 
486         writeMapXml(val, out, callback);
487 
488         out.endTag(null, "map");
489     }
490 
491     /**
492      * Flatten a Map into an XmlSerializer.  The map can later be read back
493      * with readThisMapXml(). This method presumes that the start tag and
494      * name attribute have already been written and does not write an end tag.
495      *
496      * @param val The map to be flattened.
497      * @param out XmlSerializer to write the map into.
498      *
499      * @see #writeMapXml(Map, OutputStream)
500      * @see #writeListXml
501      * @see #writeValueXml
502      * @see #readMapXml
503      *
504      * @hide
505      */
writeMapXml(Map val, TypedXmlSerializer out, WriteMapCallback callback)506     public static final void writeMapXml(Map val, TypedXmlSerializer out,
507             WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
508         if (val == null) {
509             return;
510         }
511 
512         Set s = val.entrySet();
513         Iterator i = s.iterator();
514 
515         while (i.hasNext()) {
516             Map.Entry e = (Map.Entry)i.next();
517             writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
518         }
519     }
520 
521     /**
522      * Flatten a List into an XmlSerializer.  The list can later be read back
523      * with readThisListXml().
524      *
525      * @param val The list to be flattened.
526      * @param name Name attribute to include with this list's tag, or null for
527      *             none.
528      * @param out XmlSerializer to write the list into.
529      *
530      * @see #writeListXml(List, OutputStream)
531      * @see #writeMapXml
532      * @see #writeValueXml
533      * @see #readListXml
534      */
writeListXml(List val, String name, TypedXmlSerializer out)535     public static final void writeListXml(List val, String name, TypedXmlSerializer out)
536     throws XmlPullParserException, java.io.IOException
537     {
538         if (val == null) {
539             out.startTag(null, "null");
540             out.endTag(null, "null");
541             return;
542         }
543 
544         out.startTag(null, "list");
545         if (name != null) {
546             out.attribute(null, "name", name);
547         }
548 
549         int N = val.size();
550         int i=0;
551         while (i < N) {
552             writeValueXml(val.get(i), null, out);
553             i++;
554         }
555 
556         out.endTag(null, "list");
557     }
558 
writeSetXml(Set val, String name, TypedXmlSerializer out)559     public static final void writeSetXml(Set val, String name, TypedXmlSerializer out)
560             throws XmlPullParserException, java.io.IOException {
561         if (val == null) {
562             out.startTag(null, "null");
563             out.endTag(null, "null");
564             return;
565         }
566 
567         out.startTag(null, "set");
568         if (name != null) {
569             out.attribute(null, "name", name);
570         }
571 
572         for (Object v : val) {
573             writeValueXml(v, null, out);
574         }
575 
576         out.endTag(null, "set");
577     }
578 
579     /**
580      * Flatten a byte[] into an XmlSerializer.  The list can later be read back
581      * with readThisByteArrayXml().
582      *
583      * @param val The byte array to be flattened.
584      * @param name Name attribute to include with this array's tag, or null for
585      *             none.
586      * @param out XmlSerializer to write the array into.
587      *
588      * @see #writeMapXml
589      * @see #writeValueXml
590      */
writeByteArrayXml(byte[] val, String name, TypedXmlSerializer out)591     public static final void writeByteArrayXml(byte[] val, String name,
592             TypedXmlSerializer out)
593             throws XmlPullParserException, java.io.IOException {
594 
595         if (val == null) {
596             out.startTag(null, "null");
597             out.endTag(null, "null");
598             return;
599         }
600 
601         out.startTag(null, "byte-array");
602         if (name != null) {
603             out.attribute(null, "name", name);
604         }
605 
606         final int N = val.length;
607         out.attributeInt(null, "num", N);
608 
609         out.text(HexEncoding.encodeToString(val).toLowerCase());
610 
611         out.endTag(null, "byte-array");
612     }
613 
614     /**
615      * Flatten an int[] into an XmlSerializer.  The list can later be read back
616      * with readThisIntArrayXml().
617      *
618      * @param val The int array to be flattened.
619      * @param name Name attribute to include with this array's tag, or null for
620      *             none.
621      * @param out XmlSerializer to write the array into.
622      *
623      * @see #writeMapXml
624      * @see #writeValueXml
625      * @see #readThisIntArrayXml
626      */
writeIntArrayXml(int[] val, String name, TypedXmlSerializer out)627     public static final void writeIntArrayXml(int[] val, String name,
628             TypedXmlSerializer out)
629             throws XmlPullParserException, java.io.IOException {
630 
631         if (val == null) {
632             out.startTag(null, "null");
633             out.endTag(null, "null");
634             return;
635         }
636 
637         out.startTag(null, "int-array");
638         if (name != null) {
639             out.attribute(null, "name", name);
640         }
641 
642         final int N = val.length;
643         out.attributeInt(null, "num", N);
644 
645         for (int i=0; i<N; i++) {
646             out.startTag(null, "item");
647             out.attributeInt(null, "value", val[i]);
648             out.endTag(null, "item");
649         }
650 
651         out.endTag(null, "int-array");
652     }
653 
654     /**
655      * Flatten a long[] into an XmlSerializer.  The list can later be read back
656      * with readThisLongArrayXml().
657      *
658      * @param val The long array to be flattened.
659      * @param name Name attribute to include with this array's tag, or null for
660      *             none.
661      * @param out XmlSerializer to write the array into.
662      *
663      * @see #writeMapXml
664      * @see #writeValueXml
665      * @see #readThisIntArrayXml
666      */
writeLongArrayXml(long[] val, String name, TypedXmlSerializer out)667     public static final void writeLongArrayXml(long[] val, String name, TypedXmlSerializer out)
668             throws XmlPullParserException, java.io.IOException {
669 
670         if (val == null) {
671             out.startTag(null, "null");
672             out.endTag(null, "null");
673             return;
674         }
675 
676         out.startTag(null, "long-array");
677         if (name != null) {
678             out.attribute(null, "name", name);
679         }
680 
681         final int N = val.length;
682         out.attributeInt(null, "num", N);
683 
684         for (int i=0; i<N; i++) {
685             out.startTag(null, "item");
686             out.attributeLong(null, "value", val[i]);
687             out.endTag(null, "item");
688         }
689 
690         out.endTag(null, "long-array");
691     }
692 
693     /**
694      * Flatten a double[] into an XmlSerializer.  The list can later be read back
695      * with readThisDoubleArrayXml().
696      *
697      * @param val The double array to be flattened.
698      * @param name Name attribute to include with this array's tag, or null for
699      *             none.
700      * @param out XmlSerializer to write the array into.
701      *
702      * @see #writeMapXml
703      * @see #writeValueXml
704      * @see #readThisIntArrayXml
705      */
writeDoubleArrayXml(double[] val, String name, TypedXmlSerializer out)706     public static final void writeDoubleArrayXml(double[] val, String name, TypedXmlSerializer out)
707             throws XmlPullParserException, java.io.IOException {
708 
709         if (val == null) {
710             out.startTag(null, "null");
711             out.endTag(null, "null");
712             return;
713         }
714 
715         out.startTag(null, "double-array");
716         if (name != null) {
717             out.attribute(null, "name", name);
718         }
719 
720         final int N = val.length;
721         out.attributeInt(null, "num", N);
722 
723         for (int i=0; i<N; i++) {
724             out.startTag(null, "item");
725             out.attributeDouble(null, "value", val[i]);
726             out.endTag(null, "item");
727         }
728 
729         out.endTag(null, "double-array");
730     }
731 
732     /**
733      * Flatten a String[] into an XmlSerializer.  The list can later be read back
734      * with readThisStringArrayXml().
735      *
736      * @param val The String array to be flattened.
737      * @param name Name attribute to include with this array's tag, or null for
738      *             none.
739      * @param out XmlSerializer to write the array into.
740      *
741      * @see #writeMapXml
742      * @see #writeValueXml
743      * @see #readThisIntArrayXml
744      */
writeStringArrayXml(String[] val, String name, TypedXmlSerializer out)745     public static final void writeStringArrayXml(String[] val, String name, TypedXmlSerializer out)
746             throws XmlPullParserException, java.io.IOException {
747 
748         if (val == null) {
749             out.startTag(null, "null");
750             out.endTag(null, "null");
751             return;
752         }
753 
754         out.startTag(null, "string-array");
755         if (name != null) {
756             out.attribute(null, "name", name);
757         }
758 
759         final int N = val.length;
760         out.attributeInt(null, "num", N);
761 
762         for (int i=0; i<N; i++) {
763             out.startTag(null, "item");
764             out.attribute(null, "value", val[i]);
765             out.endTag(null, "item");
766         }
767 
768         out.endTag(null, "string-array");
769     }
770 
771     /**
772      * Flatten a boolean[] into an XmlSerializer.  The list can later be read back
773      * with readThisBooleanArrayXml().
774      *
775      * @param val The boolean array to be flattened.
776      * @param name Name attribute to include with this array's tag, or null for
777      *             none.
778      * @param out XmlSerializer to write the array into.
779      *
780      * @see #writeMapXml
781      * @see #writeValueXml
782      * @see #readThisIntArrayXml
783      */
writeBooleanArrayXml(boolean[] val, String name, TypedXmlSerializer out)784     public static final void writeBooleanArrayXml(boolean[] val, String name,
785             TypedXmlSerializer out) throws XmlPullParserException, java.io.IOException {
786 
787         if (val == null) {
788             out.startTag(null, "null");
789             out.endTag(null, "null");
790             return;
791         }
792 
793         out.startTag(null, "boolean-array");
794         if (name != null) {
795             out.attribute(null, "name", name);
796         }
797 
798         final int N = val.length;
799         out.attributeInt(null, "num", N);
800 
801         for (int i=0; i<N; i++) {
802             out.startTag(null, "item");
803             out.attributeBoolean(null, "value", val[i]);
804             out.endTag(null, "item");
805         }
806 
807         out.endTag(null, "boolean-array");
808     }
809 
810     @Deprecated
writeValueXml(Object v, String name, XmlSerializer out)811     public static final void writeValueXml(Object v, String name, XmlSerializer out)
812             throws XmlPullParserException, java.io.IOException {
813         writeValueXml(v, name, XmlUtils.makeTyped(out));
814     }
815 
816     /**
817      * Flatten an object's value into an XmlSerializer.  The value can later
818      * be read back with readThisValueXml().
819      *
820      * Currently supported value types are: null, String, Integer, Long,
821      * Float, Double Boolean, Map, List.
822      *
823      * @param v The object to be flattened.
824      * @param name Name attribute to include with this value's tag, or null
825      *             for none.
826      * @param out XmlSerializer to write the object into.
827      *
828      * @see #writeMapXml
829      * @see #writeListXml
830      * @see #readValueXml
831      */
writeValueXml(Object v, String name, TypedXmlSerializer out)832     public static final void writeValueXml(Object v, String name, TypedXmlSerializer out)
833             throws XmlPullParserException, java.io.IOException {
834         writeValueXml(v, name, out, null);
835     }
836 
837     /**
838      * Flatten an object's value into an XmlSerializer.  The value can later
839      * be read back with readThisValueXml().
840      *
841      * Currently supported value types are: null, String, Integer, Long,
842      * Float, Double Boolean, Map, List.
843      *
844      * @param v The object to be flattened.
845      * @param name Name attribute to include with this value's tag, or null
846      *             for none.
847      * @param out XmlSerializer to write the object into.
848      * @param callback Handler for Object types not recognized.
849      *
850      * @see #writeMapXml
851      * @see #writeListXml
852      * @see #readValueXml
853      */
writeValueXml(Object v, String name, TypedXmlSerializer out, WriteMapCallback callback)854     private static final void writeValueXml(Object v, String name, TypedXmlSerializer out,
855             WriteMapCallback callback)  throws XmlPullParserException, java.io.IOException {
856         if (v == null) {
857             out.startTag(null, "null");
858             if (name != null) {
859                 out.attribute(null, "name", name);
860             }
861             out.endTag(null, "null");
862             return;
863         } else if (v instanceof String) {
864             out.startTag(null, "string");
865             if (name != null) {
866                 out.attribute(null, "name", name);
867             }
868             out.text(v.toString());
869             out.endTag(null, "string");
870             return;
871         } else if (v instanceof Integer) {
872             out.startTag(null, "int");
873             if (name != null) {
874                 out.attribute(null, "name", name);
875             }
876             out.attributeInt(null, "value", (int) v);
877             out.endTag(null, "int");
878         } else if (v instanceof Long) {
879             out.startTag(null, "long");
880             if (name != null) {
881                 out.attribute(null, "name", name);
882             }
883             out.attributeLong(null, "value", (long) v);
884             out.endTag(null, "long");
885         } else if (v instanceof Float) {
886             out.startTag(null, "float");
887             if (name != null) {
888                 out.attribute(null, "name", name);
889             }
890             out.attributeFloat(null, "value", (float) v);
891             out.endTag(null, "float");
892         } else if (v instanceof Double) {
893             out.startTag(null, "double");
894             if (name != null) {
895                 out.attribute(null, "name", name);
896             }
897             out.attributeDouble(null, "value", (double) v);
898             out.endTag(null, "double");
899         } else if (v instanceof Boolean) {
900             out.startTag(null, "boolean");
901             if (name != null) {
902                 out.attribute(null, "name", name);
903             }
904             out.attributeBoolean(null, "value", (boolean) v);
905             out.endTag(null, "boolean");
906         } else if (v instanceof byte[]) {
907             writeByteArrayXml((byte[])v, name, out);
908             return;
909         } else if (v instanceof int[]) {
910             writeIntArrayXml((int[])v, name, out);
911             return;
912         } else if (v instanceof long[]) {
913             writeLongArrayXml((long[])v, name, out);
914             return;
915         } else if (v instanceof double[]) {
916             writeDoubleArrayXml((double[])v, name, out);
917             return;
918         } else if (v instanceof String[]) {
919             writeStringArrayXml((String[])v, name, out);
920             return;
921         } else if (v instanceof boolean[]) {
922             writeBooleanArrayXml((boolean[])v, name, out);
923             return;
924         } else if (v instanceof Map) {
925             writeMapXml((Map)v, name, out);
926             return;
927         } else if (v instanceof List) {
928             writeListXml((List) v, name, out);
929             return;
930         } else if (v instanceof Set) {
931             writeSetXml((Set) v, name, out);
932             return;
933         } else if (v instanceof CharSequence) {
934             // XXX This is to allow us to at least write something if
935             // we encounter styled text...  but it means we will drop all
936             // of the styling information. :(
937             out.startTag(null, "string");
938             if (name != null) {
939                 out.attribute(null, "name", name);
940             }
941             out.text(v.toString());
942             out.endTag(null, "string");
943             return;
944         } else if (callback != null) {
945             callback.writeUnknownObject(v, name, out);
946             return;
947         } else {
948             throw new RuntimeException("writeValueXml: unable to write value " + v);
949         }
950     }
951 
952     /**
953      * Read a HashMap from an InputStream containing XML.  The stream can
954      * previously have been written by writeMapXml().
955      *
956      * @param in The InputStream from which to read.
957      *
958      * @return HashMap The resulting map.
959      *
960      * @see #readListXml
961      * @see #readValueXml
962      * @see #readThisMapXml
963      * #see #writeMapXml
964      */
965     @SuppressWarnings("unchecked")
966     @UnsupportedAppUsage
readMapXml(InputStream in)967     public static final HashMap<String, ?> readMapXml(InputStream in)
968             throws XmlPullParserException, java.io.IOException {
969         TypedXmlPullParser parser = Xml.newFastPullParser();
970         parser.setInput(in, StandardCharsets.UTF_8.name());
971         return (HashMap<String, ?>) readValueXml(parser, new String[1]);
972     }
973 
974     /**
975      * Read an ArrayList from an InputStream containing XML.  The stream can
976      * previously have been written by writeListXml().
977      *
978      * @param in The InputStream from which to read.
979      *
980      * @return ArrayList The resulting list.
981      *
982      * @see #readMapXml
983      * @see #readValueXml
984      * @see #readThisListXml
985      * @see #writeListXml
986      */
readListXml(InputStream in)987     public static final ArrayList readListXml(InputStream in)
988             throws XmlPullParserException, java.io.IOException {
989         TypedXmlPullParser parser = Xml.newFastPullParser();
990         parser.setInput(in, StandardCharsets.UTF_8.name());
991         return (ArrayList)readValueXml(parser, new String[1]);
992     }
993 
994 
995     /**
996      * Read a HashSet from an InputStream containing XML. The stream can
997      * previously have been written by writeSetXml().
998      *
999      * @param in The InputStream from which to read.
1000      *
1001      * @return HashSet The resulting set.
1002      *
1003      * @throws XmlPullParserException
1004      * @throws java.io.IOException
1005      *
1006      * @see #readValueXml
1007      * @see #readThisSetXml
1008      * @see #writeSetXml
1009      */
readSetXml(InputStream in)1010     public static final HashSet readSetXml(InputStream in)
1011             throws XmlPullParserException, java.io.IOException {
1012         TypedXmlPullParser parser = Xml.newFastPullParser();
1013         parser.setInput(in, StandardCharsets.UTF_8.name());
1014         return (HashSet) readValueXml(parser, new String[1]);
1015     }
1016 
1017     /**
1018      * Read a HashMap object from an XmlPullParser.  The XML data could
1019      * previously have been generated by writeMapXml().  The XmlPullParser
1020      * must be positioned <em>after</em> the tag that begins the map.
1021      *
1022      * @param parser The XmlPullParser from which to read the map data.
1023      * @param endTag Name of the tag that will end the map, usually "map".
1024      * @param name An array of one string, used to return the name attribute
1025      *             of the map's tag.
1026      *
1027      * @return HashMap The newly generated map.
1028      *
1029      * @see #readMapXml
1030      */
readThisMapXml(TypedXmlPullParser parser, String endTag, String[] name)1031     public static final HashMap<String, ?> readThisMapXml(TypedXmlPullParser parser, String endTag,
1032             String[] name) throws XmlPullParserException, java.io.IOException {
1033         return readThisMapXml(parser, endTag, name, null);
1034     }
1035 
1036     /**
1037      * Read a HashMap object from an XmlPullParser.  The XML data could
1038      * previously have been generated by writeMapXml().  The XmlPullParser
1039      * must be positioned <em>after</em> the tag that begins the map.
1040      *
1041      * @param parser The XmlPullParser from which to read the map data.
1042      * @param endTag Name of the tag that will end the map, usually "map".
1043      * @param name An array of one string, used to return the name attribute
1044      *             of the map's tag.
1045      *
1046      * @return HashMap The newly generated map.
1047      *
1048      * @see #readMapXml
1049      * @hide
1050      */
readThisMapXml(TypedXmlPullParser parser, String endTag, String[] name, ReadMapCallback callback)1051     public static final HashMap<String, ?> readThisMapXml(TypedXmlPullParser parser, String endTag,
1052             String[] name, ReadMapCallback callback)
1053             throws XmlPullParserException, java.io.IOException {
1054         HashMap<String, Object> map = new HashMap<String, Object>();
1055 
1056         int eventType = parser.getEventType();
1057         do {
1058             if (eventType == parser.START_TAG) {
1059                 Object val = readThisValueXml(parser, name, callback, false);
1060                 map.put(name[0], val);
1061             } else if (eventType == parser.END_TAG) {
1062                 if (parser.getName().equals(endTag)) {
1063                     return map;
1064                 }
1065                 throw new XmlPullParserException(
1066                     "Expected " + endTag + " end tag at: " + parser.getName());
1067             }
1068             eventType = parser.next();
1069         } while (eventType != parser.END_DOCUMENT);
1070 
1071         throw new XmlPullParserException(
1072             "Document ended before " + endTag + " end tag");
1073     }
1074 
1075     /**
1076      * Like {@link #readThisMapXml}, but returns an ArrayMap instead of HashMap.
1077      * @hide
1078      */
readThisArrayMapXml(TypedXmlPullParser parser, String endTag, String[] name, ReadMapCallback callback)1079     public static final ArrayMap<String, ?> readThisArrayMapXml(TypedXmlPullParser parser,
1080             String endTag, String[] name, ReadMapCallback callback)
1081             throws XmlPullParserException, java.io.IOException {
1082         ArrayMap<String, Object> map = new ArrayMap<>();
1083 
1084         int eventType = parser.getEventType();
1085         do {
1086             if (eventType == parser.START_TAG) {
1087                 Object val = readThisValueXml(parser, name, callback, true);
1088                 map.put(name[0], val);
1089             } else if (eventType == parser.END_TAG) {
1090                 if (parser.getName().equals(endTag)) {
1091                     return map;
1092                 }
1093                 throw new XmlPullParserException(
1094                     "Expected " + endTag + " end tag at: " + parser.getName());
1095             }
1096             eventType = parser.next();
1097         } while (eventType != parser.END_DOCUMENT);
1098 
1099         throw new XmlPullParserException(
1100             "Document ended before " + endTag + " end tag");
1101     }
1102 
1103     /**
1104      * Read an ArrayList object from an XmlPullParser.  The XML data could
1105      * previously have been generated by writeListXml().  The XmlPullParser
1106      * must be positioned <em>after</em> the tag that begins the list.
1107      *
1108      * @param parser The XmlPullParser from which to read the list data.
1109      * @param endTag Name of the tag that will end the list, usually "list".
1110      * @param name An array of one string, used to return the name attribute
1111      *             of the list's tag.
1112      *
1113      * @return HashMap The newly generated list.
1114      *
1115      * @see #readListXml
1116      */
readThisListXml(TypedXmlPullParser parser, String endTag, String[] name)1117     public static final ArrayList readThisListXml(TypedXmlPullParser parser, String endTag,
1118             String[] name) throws XmlPullParserException, java.io.IOException {
1119         return readThisListXml(parser, endTag, name, null, false);
1120     }
1121 
1122     /**
1123      * Read an ArrayList object from an XmlPullParser.  The XML data could
1124      * previously have been generated by writeListXml().  The XmlPullParser
1125      * must be positioned <em>after</em> the tag that begins the list.
1126      *
1127      * @param parser The XmlPullParser from which to read the list data.
1128      * @param endTag Name of the tag that will end the list, usually "list".
1129      * @param name An array of one string, used to return the name attribute
1130      *             of the list's tag.
1131      *
1132      * @return HashMap The newly generated list.
1133      *
1134      * @see #readListXml
1135      */
readThisListXml(TypedXmlPullParser parser, String endTag, String[] name, ReadMapCallback callback, boolean arrayMap)1136     private static final ArrayList readThisListXml(TypedXmlPullParser parser, String endTag,
1137             String[] name, ReadMapCallback callback, boolean arrayMap)
1138             throws XmlPullParserException, java.io.IOException {
1139         ArrayList list = new ArrayList();
1140 
1141         int eventType = parser.getEventType();
1142         do {
1143             if (eventType == parser.START_TAG) {
1144                 Object val = readThisValueXml(parser, name, callback, arrayMap);
1145                 list.add(val);
1146                 //System.out.println("Adding to list: " + val);
1147             } else if (eventType == parser.END_TAG) {
1148                 if (parser.getName().equals(endTag)) {
1149                     return list;
1150                 }
1151                 throw new XmlPullParserException(
1152                     "Expected " + endTag + " end tag at: " + parser.getName());
1153             }
1154             eventType = parser.next();
1155         } while (eventType != parser.END_DOCUMENT);
1156 
1157         throw new XmlPullParserException(
1158             "Document ended before " + endTag + " end tag");
1159     }
1160 
1161     /**
1162      * Read a HashSet object from an XmlPullParser. The XML data could previously
1163      * have been generated by writeSetXml(). The XmlPullParser must be positioned
1164      * <em>after</em> the tag that begins the set.
1165      *
1166      * @param parser The XmlPullParser from which to read the set data.
1167      * @param endTag Name of the tag that will end the set, usually "set".
1168      * @param name An array of one string, used to return the name attribute
1169      *             of the set's tag.
1170      *
1171      * @return HashSet The newly generated set.
1172      *
1173      * @throws XmlPullParserException
1174      * @throws java.io.IOException
1175      *
1176      * @see #readSetXml
1177      */
readThisSetXml(TypedXmlPullParser parser, String endTag, String[] name)1178     public static final HashSet readThisSetXml(TypedXmlPullParser parser, String endTag,
1179             String[] name) throws XmlPullParserException, java.io.IOException {
1180         return readThisSetXml(parser, endTag, name, null, false);
1181     }
1182 
1183     /**
1184      * Read a HashSet object from an XmlPullParser. The XML data could previously
1185      * have been generated by writeSetXml(). The XmlPullParser must be positioned
1186      * <em>after</em> the tag that begins the set.
1187      *
1188      * @param parser The XmlPullParser from which to read the set data.
1189      * @param endTag Name of the tag that will end the set, usually "set".
1190      * @param name An array of one string, used to return the name attribute
1191      *             of the set's tag.
1192      *
1193      * @return HashSet The newly generated set.
1194      *
1195      * @throws XmlPullParserException
1196      * @throws java.io.IOException
1197      *
1198      * @see #readSetXml
1199      * @hide
1200      */
readThisSetXml(TypedXmlPullParser parser, String endTag, String[] name, ReadMapCallback callback, boolean arrayMap)1201     private static final HashSet readThisSetXml(TypedXmlPullParser parser, String endTag,
1202             String[] name, ReadMapCallback callback, boolean arrayMap)
1203             throws XmlPullParserException, java.io.IOException {
1204         HashSet set = new HashSet();
1205 
1206         int eventType = parser.getEventType();
1207         do {
1208             if (eventType == parser.START_TAG) {
1209                 Object val = readThisValueXml(parser, name, callback, arrayMap);
1210                 set.add(val);
1211                 //System.out.println("Adding to set: " + val);
1212             } else if (eventType == parser.END_TAG) {
1213                 if (parser.getName().equals(endTag)) {
1214                     return set;
1215                 }
1216                 throw new XmlPullParserException(
1217                         "Expected " + endTag + " end tag at: " + parser.getName());
1218             }
1219             eventType = parser.next();
1220         } while (eventType != parser.END_DOCUMENT);
1221 
1222         throw new XmlPullParserException(
1223                 "Document ended before " + endTag + " end tag");
1224     }
1225 
1226     /**
1227      * Read a byte[] object from an XmlPullParser.  The XML data could
1228      * previously have been generated by writeByteArrayXml().  The XmlPullParser
1229      * must be positioned <em>after</em> the tag that begins the list.
1230      *
1231      * @param parser The XmlPullParser from which to read the list data.
1232      * @param endTag Name of the tag that will end the list, usually "list".
1233      * @param name An array of one string, used to return the name attribute
1234      *             of the list's tag.
1235      *
1236      * @return Returns a newly generated byte[].
1237      *
1238      * @see #writeByteArrayXml
1239      */
readThisByteArrayXml(TypedXmlPullParser parser, String endTag, String[] name)1240     public static final byte[] readThisByteArrayXml(TypedXmlPullParser parser,
1241             String endTag, String[] name)
1242             throws XmlPullParserException, java.io.IOException {
1243 
1244         int num = parser.getAttributeInt(null, "num");
1245 
1246         // 0 len byte array does not have a text in the XML tag. So, initialize to 0 len array.
1247         // For all other array lens, HexEncoding.decode() below overrides the array.
1248         byte[] array = new byte[0];
1249 
1250         int eventType = parser.getEventType();
1251         do {
1252             if (eventType == parser.TEXT) {
1253                 if (num > 0) {
1254                     String values = parser.getText();
1255                     if (values == null || values.length() != num * 2) {
1256                         throw new XmlPullParserException(
1257                                 "Invalid value found in byte-array: " + values);
1258                     }
1259                     array = HexEncoding.decode(values);
1260                 }
1261             } else if (eventType == parser.END_TAG) {
1262                 if (parser.getName().equals(endTag)) {
1263                     return array;
1264                 } else {
1265                     throw new XmlPullParserException(
1266                             "Expected " + endTag + " end tag at: "
1267                                     + parser.getName());
1268                 }
1269             }
1270             eventType = parser.next();
1271         } while (eventType != parser.END_DOCUMENT);
1272 
1273         throw new XmlPullParserException(
1274                 "Document ended before " + endTag + " end tag");
1275     }
1276 
1277     /**
1278      * Read an int[] object from an XmlPullParser.  The XML data could
1279      * previously have been generated by writeIntArrayXml().  The XmlPullParser
1280      * must be positioned <em>after</em> the tag that begins the list.
1281      *
1282      * @param parser The XmlPullParser from which to read the list data.
1283      * @param endTag Name of the tag that will end the list, usually "list".
1284      * @param name An array of one string, used to return the name attribute
1285      *             of the list's tag.
1286      *
1287      * @return Returns a newly generated int[].
1288      *
1289      * @see #readListXml
1290      */
readThisIntArrayXml(TypedXmlPullParser parser, String endTag, String[] name)1291     public static final int[] readThisIntArrayXml(TypedXmlPullParser parser,
1292             String endTag, String[] name)
1293             throws XmlPullParserException, java.io.IOException {
1294 
1295         int num = parser.getAttributeInt(null, "num");
1296         parser.next();
1297 
1298         int[] array = new int[num];
1299         int i = 0;
1300 
1301         int eventType = parser.getEventType();
1302         do {
1303             if (eventType == parser.START_TAG) {
1304                 if (parser.getName().equals("item")) {
1305                     array[i] = parser.getAttributeInt(null, "value");
1306                 } else {
1307                     throw new XmlPullParserException(
1308                             "Expected item tag at: " + parser.getName());
1309                 }
1310             } else if (eventType == parser.END_TAG) {
1311                 if (parser.getName().equals(endTag)) {
1312                     return array;
1313                 } else if (parser.getName().equals("item")) {
1314                     i++;
1315                 } else {
1316                     throw new XmlPullParserException(
1317                         "Expected " + endTag + " end tag at: "
1318                         + parser.getName());
1319                 }
1320             }
1321             eventType = parser.next();
1322         } while (eventType != parser.END_DOCUMENT);
1323 
1324         throw new XmlPullParserException(
1325             "Document ended before " + endTag + " end tag");
1326     }
1327 
1328     /**
1329      * Read a long[] object from an XmlPullParser.  The XML data could
1330      * previously have been generated by writeLongArrayXml().  The XmlPullParser
1331      * must be positioned <em>after</em> the tag that begins the list.
1332      *
1333      * @param parser The XmlPullParser from which to read the list data.
1334      * @param endTag Name of the tag that will end the list, usually "list".
1335      * @param name An array of one string, used to return the name attribute
1336      *             of the list's tag.
1337      *
1338      * @return Returns a newly generated long[].
1339      *
1340      * @see #readListXml
1341      */
readThisLongArrayXml(TypedXmlPullParser parser, String endTag, String[] name)1342     public static final long[] readThisLongArrayXml(TypedXmlPullParser parser,
1343             String endTag, String[] name)
1344             throws XmlPullParserException, java.io.IOException {
1345 
1346         int num = parser.getAttributeInt(null, "num");
1347         parser.next();
1348 
1349         long[] array = new long[num];
1350         int i = 0;
1351 
1352         int eventType = parser.getEventType();
1353         do {
1354             if (eventType == parser.START_TAG) {
1355                 if (parser.getName().equals("item")) {
1356                     array[i] = parser.getAttributeLong(null, "value");
1357                 } else {
1358                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1359                 }
1360             } else if (eventType == parser.END_TAG) {
1361                 if (parser.getName().equals(endTag)) {
1362                     return array;
1363                 } else if (parser.getName().equals("item")) {
1364                     i++;
1365                 } else {
1366                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1367                             parser.getName());
1368                 }
1369             }
1370             eventType = parser.next();
1371         } while (eventType != parser.END_DOCUMENT);
1372 
1373         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1374     }
1375 
1376     /**
1377      * Read a double[] object from an XmlPullParser.  The XML data could
1378      * previously have been generated by writeDoubleArrayXml().  The XmlPullParser
1379      * must be positioned <em>after</em> the tag that begins the list.
1380      *
1381      * @param parser The XmlPullParser from which to read the list data.
1382      * @param endTag Name of the tag that will end the list, usually "double-array".
1383      * @param name An array of one string, used to return the name attribute
1384      *             of the list's tag.
1385      *
1386      * @return Returns a newly generated double[].
1387      *
1388      * @see #readListXml
1389      */
readThisDoubleArrayXml(TypedXmlPullParser parser, String endTag, String[] name)1390     public static final double[] readThisDoubleArrayXml(TypedXmlPullParser parser, String endTag,
1391             String[] name) throws XmlPullParserException, java.io.IOException {
1392 
1393         int num = parser.getAttributeInt(null, "num");
1394         parser.next();
1395 
1396         double[] array = new double[num];
1397         int i = 0;
1398 
1399         int eventType = parser.getEventType();
1400         do {
1401             if (eventType == parser.START_TAG) {
1402                 if (parser.getName().equals("item")) {
1403                     array[i] = parser.getAttributeDouble(null, "value");
1404                 } else {
1405                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1406                 }
1407             } else if (eventType == parser.END_TAG) {
1408                 if (parser.getName().equals(endTag)) {
1409                     return array;
1410                 } else if (parser.getName().equals("item")) {
1411                     i++;
1412                 } else {
1413                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1414                             parser.getName());
1415                 }
1416             }
1417             eventType = parser.next();
1418         } while (eventType != parser.END_DOCUMENT);
1419 
1420         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1421     }
1422 
1423     /**
1424      * Read a String[] object from an XmlPullParser.  The XML data could
1425      * previously have been generated by writeStringArrayXml().  The XmlPullParser
1426      * must be positioned <em>after</em> the tag that begins the list.
1427      *
1428      * @param parser The XmlPullParser from which to read the list data.
1429      * @param endTag Name of the tag that will end the list, usually "string-array".
1430      * @param name An array of one string, used to return the name attribute
1431      *             of the list's tag.
1432      *
1433      * @return Returns a newly generated String[].
1434      *
1435      * @see #readListXml
1436      */
readThisStringArrayXml(TypedXmlPullParser parser, String endTag, String[] name)1437     public static final String[] readThisStringArrayXml(TypedXmlPullParser parser, String endTag,
1438             String[] name) throws XmlPullParserException, java.io.IOException {
1439 
1440         int num = parser.getAttributeInt(null, "num");
1441         parser.next();
1442 
1443         String[] array = new String[num];
1444         int i = 0;
1445 
1446         int eventType = parser.getEventType();
1447         do {
1448             if (eventType == parser.START_TAG) {
1449                 if (parser.getName().equals("item")) {
1450                     array[i] = parser.getAttributeValue(null, "value");
1451                 } else {
1452                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1453                 }
1454             } else if (eventType == parser.END_TAG) {
1455                 if (parser.getName().equals(endTag)) {
1456                     return array;
1457                 } else if (parser.getName().equals("item")) {
1458                     i++;
1459                 } else {
1460                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1461                             parser.getName());
1462                 }
1463             }
1464             eventType = parser.next();
1465         } while (eventType != parser.END_DOCUMENT);
1466 
1467         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1468     }
1469 
1470     /**
1471      * Read a boolean[] object from an XmlPullParser.  The XML data could
1472      * previously have been generated by writeBooleanArrayXml().  The XmlPullParser
1473      * must be positioned <em>after</em> the tag that begins the list.
1474      *
1475      * @param parser The XmlPullParser from which to read the list data.
1476      * @param endTag Name of the tag that will end the list, usually "string-array".
1477      * @param name An array of one string, used to return the name attribute
1478      *             of the list's tag.
1479      *
1480      * @return Returns a newly generated boolean[].
1481      *
1482      * @see #readListXml
1483      */
readThisBooleanArrayXml(TypedXmlPullParser parser, String endTag, String[] name)1484     public static final boolean[] readThisBooleanArrayXml(TypedXmlPullParser parser, String endTag,
1485             String[] name) throws XmlPullParserException, java.io.IOException {
1486 
1487         int num = parser.getAttributeInt(null, "num");
1488         parser.next();
1489 
1490         boolean[] array = new boolean[num];
1491         int i = 0;
1492 
1493         int eventType = parser.getEventType();
1494         do {
1495             if (eventType == parser.START_TAG) {
1496                 if (parser.getName().equals("item")) {
1497                     array[i] = parser.getAttributeBoolean(null, "value");
1498                 } else {
1499                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1500                 }
1501             } else if (eventType == parser.END_TAG) {
1502                 if (parser.getName().equals(endTag)) {
1503                     return array;
1504                 } else if (parser.getName().equals("item")) {
1505                     i++;
1506                 } else {
1507                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1508                             parser.getName());
1509                 }
1510             }
1511             eventType = parser.next();
1512         } while (eventType != parser.END_DOCUMENT);
1513 
1514         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1515     }
1516 
1517     /**
1518      * Read a flattened object from an XmlPullParser.  The XML data could
1519      * previously have been written with writeMapXml(), writeListXml(), or
1520      * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
1521      * tag that defines the value.
1522      *
1523      * @param parser The XmlPullParser from which to read the object.
1524      * @param name An array of one string, used to return the name attribute
1525      *             of the value's tag.
1526      *
1527      * @return Object The newly generated value object.
1528      *
1529      * @see #readMapXml
1530      * @see #readListXml
1531      * @see #writeValueXml
1532      */
readValueXml(TypedXmlPullParser parser, String[] name)1533     public static final Object readValueXml(TypedXmlPullParser parser, String[] name)
1534     throws XmlPullParserException, java.io.IOException
1535     {
1536         int eventType = parser.getEventType();
1537         do {
1538             if (eventType == parser.START_TAG) {
1539                 return readThisValueXml(parser, name, null, false);
1540             } else if (eventType == parser.END_TAG) {
1541                 throw new XmlPullParserException(
1542                     "Unexpected end tag at: " + parser.getName());
1543             } else if (eventType == parser.TEXT) {
1544                 throw new XmlPullParserException(
1545                     "Unexpected text: " + parser.getText());
1546             }
1547             eventType = parser.next();
1548         } while (eventType != parser.END_DOCUMENT);
1549 
1550         throw new XmlPullParserException(
1551             "Unexpected end of document");
1552     }
1553 
readThisValueXml(TypedXmlPullParser parser, String[] name, ReadMapCallback callback, boolean arrayMap)1554     private static final Object readThisValueXml(TypedXmlPullParser parser, String[] name,
1555             ReadMapCallback callback, boolean arrayMap)
1556             throws XmlPullParserException, java.io.IOException {
1557         final String valueName = parser.getAttributeValue(null, "name");
1558         final String tagName = parser.getName();
1559 
1560         //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
1561 
1562         Object res;
1563 
1564         if (tagName.equals("null")) {
1565             res = null;
1566         } else if (tagName.equals("string")) {
1567             final StringBuilder value = new StringBuilder();
1568             int eventType;
1569             while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1570                 if (eventType == parser.END_TAG) {
1571                     if (parser.getName().equals("string")) {
1572                         name[0] = valueName;
1573                         //System.out.println("Returning value for " + valueName + ": " + value);
1574                         return value.toString();
1575                     }
1576                     throw new XmlPullParserException(
1577                         "Unexpected end tag in <string>: " + parser.getName());
1578                 } else if (eventType == parser.TEXT) {
1579                     value.append(parser.getText());
1580                 } else if (eventType == parser.START_TAG) {
1581                     throw new XmlPullParserException(
1582                         "Unexpected start tag in <string>: " + parser.getName());
1583                 }
1584             }
1585             throw new XmlPullParserException(
1586                 "Unexpected end of document in <string>");
1587         } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
1588             // all work already done by readThisPrimitiveValueXml
1589         } else if (tagName.equals("byte-array")) {
1590             res = readThisByteArrayXml(parser, "byte-array", name);
1591             name[0] = valueName;
1592             //System.out.println("Returning value for " + valueName + ": " + res);
1593             return res;
1594         } else if (tagName.equals("int-array")) {
1595             res = readThisIntArrayXml(parser, "int-array", name);
1596             name[0] = valueName;
1597             //System.out.println("Returning value for " + valueName + ": " + res);
1598             return res;
1599         } else if (tagName.equals("long-array")) {
1600             res = readThisLongArrayXml(parser, "long-array", name);
1601             name[0] = valueName;
1602             //System.out.println("Returning value for " + valueName + ": " + res);
1603             return res;
1604         } else if (tagName.equals("double-array")) {
1605             res = readThisDoubleArrayXml(parser, "double-array", name);
1606             name[0] = valueName;
1607             //System.out.println("Returning value for " + valueName + ": " + res);
1608             return res;
1609         } else if (tagName.equals("string-array")) {
1610             res = readThisStringArrayXml(parser, "string-array", name);
1611             name[0] = valueName;
1612             //System.out.println("Returning value for " + valueName + ": " + res);
1613             return res;
1614         } else if (tagName.equals("boolean-array")) {
1615             res = readThisBooleanArrayXml(parser, "boolean-array", name);
1616             name[0] = valueName;
1617             //System.out.println("Returning value for " + valueName + ": " + res);
1618             return res;
1619         } else if (tagName.equals("map")) {
1620             parser.next();
1621             res = arrayMap
1622                     ? readThisArrayMapXml(parser, "map", name, callback)
1623                     : readThisMapXml(parser, "map", name, callback);
1624             name[0] = valueName;
1625             //System.out.println("Returning value for " + valueName + ": " + res);
1626             return res;
1627         } else if (tagName.equals("list")) {
1628             parser.next();
1629             res = readThisListXml(parser, "list", name, callback, arrayMap);
1630             name[0] = valueName;
1631             //System.out.println("Returning value for " + valueName + ": " + res);
1632             return res;
1633         } else if (tagName.equals("set")) {
1634             parser.next();
1635             res = readThisSetXml(parser, "set", name, callback, arrayMap);
1636             name[0] = valueName;
1637             //System.out.println("Returning value for " + valueName + ": " + res);
1638             return res;
1639         } else if (callback != null) {
1640             res = callback.readThisUnknownObjectXml(parser, tagName);
1641             name[0] = valueName;
1642             return res;
1643         } else {
1644             throw new XmlPullParserException("Unknown tag: " + tagName);
1645         }
1646 
1647         // Skip through to end tag.
1648         int eventType;
1649         while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1650             if (eventType == parser.END_TAG) {
1651                 if (parser.getName().equals(tagName)) {
1652                     name[0] = valueName;
1653                     //System.out.println("Returning value for " + valueName + ": " + res);
1654                     return res;
1655                 }
1656                 throw new XmlPullParserException(
1657                     "Unexpected end tag in <" + tagName + ">: " + parser.getName());
1658             } else if (eventType == parser.TEXT) {
1659                 throw new XmlPullParserException(
1660                 "Unexpected text in <" + tagName + ">: " + parser.getName());
1661             } else if (eventType == parser.START_TAG) {
1662                 throw new XmlPullParserException(
1663                     "Unexpected start tag in <" + tagName + ">: " + parser.getName());
1664             }
1665         }
1666         throw new XmlPullParserException(
1667             "Unexpected end of document in <" + tagName + ">");
1668     }
1669 
readThisPrimitiveValueXml(TypedXmlPullParser parser, String tagName)1670     private static final Object readThisPrimitiveValueXml(TypedXmlPullParser parser, String tagName)
1671             throws XmlPullParserException, java.io.IOException {
1672         if (tagName.equals("int")) {
1673             return parser.getAttributeInt(null, "value");
1674         } else if (tagName.equals("long")) {
1675             return parser.getAttributeLong(null, "value");
1676         } else if (tagName.equals("float")) {
1677             return parser.getAttributeFloat(null, "value");
1678         } else if (tagName.equals("double")) {
1679             return parser.getAttributeDouble(null, "value");
1680         } else if (tagName.equals("boolean")) {
1681             return parser.getAttributeBoolean(null, "value");
1682         } else {
1683             return null;
1684         }
1685     }
1686 
1687     @UnsupportedAppUsage
beginDocument(XmlPullParser parser, String firstElementName)1688     public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
1689     {
1690         int type;
1691         while ((type=parser.next()) != parser.START_TAG
1692                    && type != parser.END_DOCUMENT) {
1693             ;
1694         }
1695 
1696         if (type != parser.START_TAG) {
1697             throw new XmlPullParserException("No start tag found");
1698         }
1699 
1700         if (!parser.getName().equals(firstElementName)) {
1701             throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1702                     ", expected " + firstElementName);
1703         }
1704     }
1705 
1706     @UnsupportedAppUsage
nextElement(XmlPullParser parser)1707     public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
1708     {
1709         int type;
1710         while ((type=parser.next()) != parser.START_TAG
1711                    && type != parser.END_DOCUMENT) {
1712             ;
1713         }
1714     }
1715 
nextElementWithin(XmlPullParser parser, int outerDepth)1716     public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
1717             throws IOException, XmlPullParserException {
1718         for (;;) {
1719             int type = parser.next();
1720             if (type == XmlPullParser.END_DOCUMENT
1721                     || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
1722                 return false;
1723             }
1724             if (type == XmlPullParser.START_TAG
1725                     && parser.getDepth() == outerDepth + 1) {
1726                 return true;
1727             }
1728         }
1729     }
1730 
1731     @SuppressWarnings("AndroidFrameworkEfficientXml")
readIntAttribute(XmlPullParser in, String name, int defaultValue)1732     public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
1733         if (in instanceof TypedXmlPullParser) {
1734             return ((TypedXmlPullParser) in).getAttributeInt(null, name, defaultValue);
1735         }
1736         final String value = in.getAttributeValue(null, name);
1737         if (TextUtils.isEmpty(value)) {
1738             return defaultValue;
1739         }
1740         try {
1741             return Integer.parseInt(value);
1742         } catch (NumberFormatException e) {
1743             return defaultValue;
1744         }
1745     }
1746 
1747     @SuppressWarnings("AndroidFrameworkEfficientXml")
readIntAttribute(XmlPullParser in, String name)1748     public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
1749         if (in instanceof TypedXmlPullParser) {
1750             try {
1751                 return ((TypedXmlPullParser) in).getAttributeInt(null, name);
1752             } catch (XmlPullParserException e) {
1753                 throw new ProtocolException(e.getMessage());
1754             }
1755         }
1756         final String value = in.getAttributeValue(null, name);
1757         try {
1758             return Integer.parseInt(value);
1759         } catch (NumberFormatException e) {
1760             throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
1761         }
1762     }
1763 
1764     @SuppressWarnings("AndroidFrameworkEfficientXml")
writeIntAttribute(XmlSerializer out, String name, int value)1765     public static void writeIntAttribute(XmlSerializer out, String name, int value)
1766             throws IOException {
1767         if (out instanceof TypedXmlSerializer) {
1768             ((TypedXmlSerializer) out).attributeInt(null, name, value);
1769             return;
1770         }
1771         out.attribute(null, name, Integer.toString(value));
1772     }
1773 
1774     @SuppressWarnings("AndroidFrameworkEfficientXml")
readLongAttribute(XmlPullParser in, String name, long defaultValue)1775     public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
1776         if (in instanceof TypedXmlPullParser) {
1777             return ((TypedXmlPullParser) in).getAttributeLong(null, name, defaultValue);
1778         }
1779         final String value = in.getAttributeValue(null, name);
1780         if (TextUtils.isEmpty(value)) {
1781             return defaultValue;
1782         }
1783         try {
1784             return Long.parseLong(value);
1785         } catch (NumberFormatException e) {
1786             return defaultValue;
1787         }
1788     }
1789 
1790     @SuppressWarnings("AndroidFrameworkEfficientXml")
readLongAttribute(XmlPullParser in, String name)1791     public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
1792         if (in instanceof TypedXmlPullParser) {
1793             try {
1794                 return ((TypedXmlPullParser) in).getAttributeLong(null, name);
1795             } catch (XmlPullParserException e) {
1796                 throw new ProtocolException(e.getMessage());
1797             }
1798         }
1799         final String value = in.getAttributeValue(null, name);
1800         try {
1801             return Long.parseLong(value);
1802         } catch (NumberFormatException e) {
1803             throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1804         }
1805     }
1806 
1807     @SuppressWarnings("AndroidFrameworkEfficientXml")
writeLongAttribute(XmlSerializer out, String name, long value)1808     public static void writeLongAttribute(XmlSerializer out, String name, long value)
1809             throws IOException {
1810         if (out instanceof TypedXmlSerializer) {
1811             ((TypedXmlSerializer) out).attributeLong(null, name, value);
1812             return;
1813         }
1814         out.attribute(null, name, Long.toString(value));
1815     }
1816 
1817     @SuppressWarnings("AndroidFrameworkEfficientXml")
readFloatAttribute(XmlPullParser in, String name)1818     public static float readFloatAttribute(XmlPullParser in, String name) throws IOException {
1819         if (in instanceof TypedXmlPullParser) {
1820             try {
1821                 return ((TypedXmlPullParser) in).getAttributeFloat(null, name);
1822             } catch (XmlPullParserException e) {
1823                 throw new ProtocolException(e.getMessage());
1824             }
1825         }
1826         final String value = in.getAttributeValue(null, name);
1827         try {
1828             return Float.parseFloat(value);
1829         } catch (NumberFormatException e) {
1830             throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1831         }
1832     }
1833 
1834     @SuppressWarnings("AndroidFrameworkEfficientXml")
writeFloatAttribute(XmlSerializer out, String name, float value)1835     public static void writeFloatAttribute(XmlSerializer out, String name, float value)
1836             throws IOException {
1837         if (out instanceof TypedXmlSerializer) {
1838             ((TypedXmlSerializer) out).attributeFloat(null, name, value);
1839             return;
1840         }
1841         out.attribute(null, name, Float.toString(value));
1842     }
1843 
1844     @SuppressWarnings("AndroidFrameworkEfficientXml")
readBooleanAttribute(XmlPullParser in, String name)1845     public static boolean readBooleanAttribute(XmlPullParser in, String name) {
1846         return readBooleanAttribute(in, name, false);
1847     }
1848 
1849     @SuppressWarnings("AndroidFrameworkEfficientXml")
readBooleanAttribute(XmlPullParser in, String name, boolean defaultValue)1850     public static boolean readBooleanAttribute(XmlPullParser in, String name,
1851             boolean defaultValue) {
1852         if (in instanceof TypedXmlPullParser) {
1853             return ((TypedXmlPullParser) in).getAttributeBoolean(null, name, defaultValue);
1854         }
1855         final String value = in.getAttributeValue(null, name);
1856         if (TextUtils.isEmpty(value)) {
1857             return defaultValue;
1858         } else {
1859             return Boolean.parseBoolean(value);
1860         }
1861     }
1862 
1863     @SuppressWarnings("AndroidFrameworkEfficientXml")
writeBooleanAttribute(XmlSerializer out, String name, boolean value)1864     public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
1865             throws IOException {
1866         if (out instanceof TypedXmlSerializer) {
1867             ((TypedXmlSerializer) out).attributeBoolean(null, name, value);
1868             return;
1869         }
1870         out.attribute(null, name, Boolean.toString(value));
1871     }
1872 
readUriAttribute(XmlPullParser in, String name)1873     public static Uri readUriAttribute(XmlPullParser in, String name) {
1874         final String value = in.getAttributeValue(null, name);
1875         return (value != null) ? Uri.parse(value) : null;
1876     }
1877 
writeUriAttribute(XmlSerializer out, String name, Uri value)1878     public static void writeUriAttribute(XmlSerializer out, String name, Uri value)
1879             throws IOException {
1880         if (value != null) {
1881             out.attribute(null, name, value.toString());
1882         }
1883     }
1884 
readStringAttribute(XmlPullParser in, String name)1885     public static String readStringAttribute(XmlPullParser in, String name) {
1886         return in.getAttributeValue(null, name);
1887     }
1888 
writeStringAttribute(XmlSerializer out, String name, CharSequence value)1889     public static void writeStringAttribute(XmlSerializer out, String name, CharSequence value)
1890             throws IOException {
1891         if (value != null) {
1892             out.attribute(null, name, value.toString());
1893         }
1894     }
1895 
1896     @SuppressWarnings("AndroidFrameworkEfficientXml")
readByteArrayAttribute(XmlPullParser in, String name)1897     public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
1898         if (in instanceof TypedXmlPullParser) {
1899             try {
1900                 return ((TypedXmlPullParser) in).getAttributeBytesBase64(null, name);
1901             } catch (XmlPullParserException e) {
1902                 return null;
1903             }
1904         }
1905         final String value = in.getAttributeValue(null, name);
1906         if (!TextUtils.isEmpty(value)) {
1907             return Base64.decode(value, Base64.DEFAULT);
1908         } else {
1909             return null;
1910         }
1911     }
1912 
1913     @SuppressWarnings("AndroidFrameworkEfficientXml")
writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)1914     public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)
1915             throws IOException {
1916         if (value != null) {
1917             if (out instanceof TypedXmlSerializer) {
1918                 ((TypedXmlSerializer) out).attributeBytesBase64(null, name, value);
1919                 return;
1920             }
1921             out.attribute(null, name, Base64.encodeToString(value, Base64.DEFAULT));
1922         }
1923     }
1924 
readBitmapAttribute(XmlPullParser in, String name)1925     public static Bitmap readBitmapAttribute(XmlPullParser in, String name) {
1926         final byte[] value = readByteArrayAttribute(in, name);
1927         if (value != null) {
1928             return BitmapFactory.decodeByteArray(value, 0, value.length);
1929         } else {
1930             return null;
1931         }
1932     }
1933 
1934     @Deprecated
writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)1935     public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
1936             throws IOException {
1937         if (value != null) {
1938             final ByteArrayOutputStream os = new ByteArrayOutputStream();
1939             value.compress(CompressFormat.PNG, 90, os);
1940             writeByteArrayAttribute(out, name, os.toByteArray());
1941         }
1942     }
1943 
1944     /** @hide */
1945     public interface WriteMapCallback {
1946         /**
1947          * Called from writeMapXml when an Object type is not recognized. The implementer
1948          * must write out the entire element including start and end tags.
1949          *
1950          * @param v The object to be written out
1951          * @param name The mapping key for v. Must be written into the "name" attribute of the
1952          *             start tag.
1953          * @param out The XML output stream.
1954          * @throws XmlPullParserException on unrecognized Object type.
1955          * @throws IOException on XmlSerializer serialization errors.
1956          * @hide
1957          */
writeUnknownObject(Object v, String name, TypedXmlSerializer out)1958          public void writeUnknownObject(Object v, String name, TypedXmlSerializer out)
1959                  throws XmlPullParserException, IOException;
1960     }
1961 
1962     /** @hide */
1963     public interface ReadMapCallback {
1964         /**
1965          * Called from readThisMapXml when a START_TAG is not recognized. The input stream
1966          * is positioned within the start tag so that attributes can be read using in.getAttribute.
1967          *
1968          * @param in the XML input stream
1969          * @param tag the START_TAG that was not recognized.
1970          * @return the Object parsed from the stream which will be put into the map.
1971          * @throws XmlPullParserException if the START_TAG is not recognized.
1972          * @throws IOException on XmlPullParser serialization errors.
1973          * @hide
1974          */
readThisUnknownObjectXml(TypedXmlPullParser in, String tag)1975         public Object readThisUnknownObjectXml(TypedXmlPullParser in, String tag)
1976                 throws XmlPullParserException, IOException;
1977     }
1978 }
1979