• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.os;
18 
19 import android.util.ArrayMap;
20 import com.android.internal.util.XmlUtils;
21 import org.xmlpull.v1.XmlPullParser;
22 import org.xmlpull.v1.XmlPullParserException;
23 import org.xmlpull.v1.XmlSerializer;
24 
25 import java.io.IOException;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Set;
29 
30 /**
31  * A mapping from String values to various types that can be saved to persistent and later
32  * restored.
33  *
34  */
35 public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
36         XmlUtils.WriteMapCallback {
37     private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
38     public static final PersistableBundle EMPTY;
39     static final Parcel EMPTY_PARCEL;
40 
41     static {
42         EMPTY = new PersistableBundle();
43         EMPTY.mMap = ArrayMap.EMPTY;
44         EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
45     }
46 
47     /**
48      * Constructs a new, empty PersistableBundle.
49      */
PersistableBundle()50     public PersistableBundle() {
51         super();
52     }
53 
54     /**
55      * Constructs a new, empty PersistableBundle sized to hold the given number of
56      * elements. The PersistableBundle will grow as needed.
57      *
58      * @param capacity the initial capacity of the PersistableBundle
59      */
PersistableBundle(int capacity)60     public PersistableBundle(int capacity) {
61         super(capacity);
62     }
63 
64     /**
65      * Constructs a PersistableBundle containing a copy of the mappings from the given
66      * PersistableBundle.
67      *
68      * @param b a PersistableBundle to be copied.
69      */
PersistableBundle(PersistableBundle b)70     public PersistableBundle(PersistableBundle b) {
71         super(b);
72     }
73 
74     /**
75      * Constructs a PersistableBundle containing the mappings passed in.
76      *
77      * @param map a Map containing only those items that can be persisted.
78      * @throws IllegalArgumentException if any element of #map cannot be persisted.
79      */
PersistableBundle(Map<String, Object> map)80     private PersistableBundle(Map<String, Object> map) {
81         super();
82 
83         // First stuff everything in.
84         putAll(map);
85 
86         // Now verify each item throwing an exception if there is a violation.
87         Set<String> keys = map.keySet();
88         Iterator<String> iterator = keys.iterator();
89         while (iterator.hasNext()) {
90             String key = iterator.next();
91             Object value = map.get(key);
92             if (value instanceof Map) {
93                 // Fix up any Maps by replacing them with PersistableBundles.
94                 putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value));
95             } else if (!(value instanceof Integer) && !(value instanceof Long) &&
96                     !(value instanceof Double) && !(value instanceof String) &&
97                     !(value instanceof int[]) && !(value instanceof long[]) &&
98                     !(value instanceof double[]) && !(value instanceof String[]) &&
99                     !(value instanceof PersistableBundle) && (value != null) &&
100                     !(value instanceof Boolean) && !(value instanceof boolean[])) {
101                 throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key +
102                         " value=" + value);
103             }
104         }
105     }
106 
PersistableBundle(Parcel parcelledData, int length)107     /* package */ PersistableBundle(Parcel parcelledData, int length) {
108         super(parcelledData, length);
109     }
110 
111     /**
112      * Make a PersistableBundle for a single key/value pair.
113      *
114      * @hide
115      */
forPair(String key, String value)116     public static PersistableBundle forPair(String key, String value) {
117         PersistableBundle b = new PersistableBundle(1);
118         b.putString(key, value);
119         return b;
120     }
121 
122     /**
123      * Clones the current PersistableBundle. The internal map is cloned, but the keys and
124      * values to which it refers are copied by reference.
125      */
126     @Override
clone()127     public Object clone() {
128         return new PersistableBundle(this);
129     }
130 
131     /**
132      * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
133      * any existing value for the given key.  Either key or value may be null.
134      *
135      * @param key a String, or null
136      * @param value a Bundle object, or null
137      */
putPersistableBundle(String key, PersistableBundle value)138     public void putPersistableBundle(String key, PersistableBundle value) {
139         unparcel();
140         mMap.put(key, value);
141     }
142 
143     /**
144      * Returns the value associated with the given key, or null if
145      * no mapping of the desired type exists for the given key or a null
146      * value is explicitly associated with the key.
147      *
148      * @param key a String, or null
149      * @return a Bundle value, or null
150      */
getPersistableBundle(String key)151     public PersistableBundle getPersistableBundle(String key) {
152         unparcel();
153         Object o = mMap.get(key);
154         if (o == null) {
155             return null;
156         }
157         try {
158             return (PersistableBundle) o;
159         } catch (ClassCastException e) {
160             typeWarning(key, o, "Bundle", e);
161             return null;
162         }
163     }
164 
165     public static final Parcelable.Creator<PersistableBundle> CREATOR =
166             new Parcelable.Creator<PersistableBundle>() {
167                 @Override
168                 public PersistableBundle createFromParcel(Parcel in) {
169                     return in.readPersistableBundle();
170                 }
171 
172                 @Override
173                 public PersistableBundle[] newArray(int size) {
174                     return new PersistableBundle[size];
175                 }
176             };
177 
178     /** @hide */
179     @Override
writeUnknownObject(Object v, String name, XmlSerializer out)180     public void writeUnknownObject(Object v, String name, XmlSerializer out)
181             throws XmlPullParserException, IOException {
182         if (v instanceof PersistableBundle) {
183             out.startTag(null, TAG_PERSISTABLEMAP);
184             out.attribute(null, "name", name);
185             ((PersistableBundle) v).saveToXml(out);
186             out.endTag(null, TAG_PERSISTABLEMAP);
187         } else {
188             throw new XmlPullParserException("Unknown Object o=" + v);
189         }
190     }
191 
192     /** @hide */
saveToXml(XmlSerializer out)193     public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
194         unparcel();
195         XmlUtils.writeMapXml(mMap, out, this);
196     }
197 
198     /** @hide */
199     static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
200         @Override
readThisUnknownObjectXml(XmlPullParser in, String tag)201         public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
202                 throws XmlPullParserException, IOException {
203             if (TAG_PERSISTABLEMAP.equals(tag)) {
204                 return restoreFromXml(in);
205             }
206             throw new XmlPullParserException("Unknown tag=" + tag);
207         }
208     }
209 
210     /**
211      * Report the nature of this Parcelable's contents
212      */
213     @Override
describeContents()214     public int describeContents() {
215         return 0;
216     }
217 
218     /**
219      * Writes the PersistableBundle contents to a Parcel, typically in order for
220      * it to be passed through an IBinder connection.
221      * @param parcel The parcel to copy this bundle to.
222      */
223     @Override
writeToParcel(Parcel parcel, int flags)224     public void writeToParcel(Parcel parcel, int flags) {
225         final boolean oldAllowFds = parcel.pushAllowFds(false);
226         try {
227             writeToParcelInner(parcel, flags);
228         } finally {
229             parcel.restoreAllowFds(oldAllowFds);
230         }
231     }
232 
233     /** @hide */
restoreFromXml(XmlPullParser in)234     public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
235             XmlPullParserException {
236         final int outerDepth = in.getDepth();
237         final String startTag = in.getName();
238         final String[] tagName = new String[1];
239         int event;
240         while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
241                 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
242             if (event == XmlPullParser.START_TAG) {
243                 return new PersistableBundle((Map<String, Object>)
244                         XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback()));
245             }
246         }
247         return EMPTY;
248     }
249 
250     @Override
toString()251     synchronized public String toString() {
252         if (mParcelledData != null) {
253             if (mParcelledData == EMPTY_PARCEL) {
254                 return "PersistableBundle[EMPTY_PARCEL]";
255             } else {
256                 return "PersistableBundle[mParcelledData.dataSize=" +
257                         mParcelledData.dataSize() + "]";
258             }
259         }
260         return "PersistableBundle[" + mMap.toString() + "]";
261     }
262 
263 }
264