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