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