• 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.annotation.Nullable;
20 import android.util.ArrayMap;
21 
22 import com.android.internal.util.XmlUtils;
23 
24 import org.xmlpull.v1.XmlPullParser;
25 import org.xmlpull.v1.XmlPullParserException;
26 import org.xmlpull.v1.XmlSerializer;
27 
28 import java.io.IOException;
29 
30 /**
31  * A mapping from String keys to values of various types. The set of types
32  * supported by this class is purposefully restricted to simple objects that can
33  * safely be persisted to and restored from disk.
34  *
35  * @see Bundle
36  */
37 public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
38         XmlUtils.WriteMapCallback {
39     private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
40     public static final PersistableBundle EMPTY;
41 
42     static {
43         EMPTY = new PersistableBundle();
44         EMPTY.mMap = ArrayMap.EMPTY;
45     }
46 
47     /** @hide */
isValidType(Object value)48     public static boolean isValidType(Object value) {
49         return (value instanceof Integer) || (value instanceof Long) ||
50                 (value instanceof Double) || (value instanceof String) ||
51                 (value instanceof int[]) || (value instanceof long[]) ||
52                 (value instanceof double[]) || (value instanceof String[]) ||
53                 (value instanceof PersistableBundle) || (value == null) ||
54                 (value instanceof Boolean) || (value instanceof boolean[]);
55     }
56 
57     /**
58      * Constructs a new, empty PersistableBundle.
59      */
PersistableBundle()60     public PersistableBundle() {
61         super();
62         mFlags = FLAG_DEFUSABLE;
63     }
64 
65     /**
66      * Constructs a new, empty PersistableBundle sized to hold the given number of
67      * elements. The PersistableBundle will grow as needed.
68      *
69      * @param capacity the initial capacity of the PersistableBundle
70      */
PersistableBundle(int capacity)71     public PersistableBundle(int capacity) {
72         super(capacity);
73         mFlags = FLAG_DEFUSABLE;
74     }
75 
76     /**
77      * Constructs a PersistableBundle containing a copy of the mappings from the given
78      * PersistableBundle.
79      *
80      * @param b a PersistableBundle to be copied.
81      */
PersistableBundle(PersistableBundle b)82     public PersistableBundle(PersistableBundle b) {
83         super(b);
84         mFlags = b.mFlags;
85     }
86 
87 
88     /**
89      * Constructs a PersistableBundle from a Bundle.
90      *
91      * @param b a Bundle to be copied.
92      *
93      * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
94      *
95      * @hide
96      */
PersistableBundle(Bundle b)97     public PersistableBundle(Bundle b) {
98         this(b.getMap());
99     }
100 
101     /**
102      * Constructs a PersistableBundle containing the mappings passed in.
103      *
104      * @param map a Map containing only those items that can be persisted.
105      * @throws IllegalArgumentException if any element of #map cannot be persisted.
106      */
PersistableBundle(ArrayMap<String, Object> map)107     private PersistableBundle(ArrayMap<String, Object> map) {
108         super();
109         mFlags = FLAG_DEFUSABLE;
110 
111         // First stuff everything in.
112         putAll(map);
113 
114         // Now verify each item throwing an exception if there is a violation.
115         final int N = mMap.size();
116         for (int i=0; i<N; i++) {
117             Object value = mMap.valueAt(i);
118             if (value instanceof ArrayMap) {
119                 // Fix up any Maps by replacing them with PersistableBundles.
120                 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
121             } else if (value instanceof Bundle) {
122                 mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
123             } else if (!isValidType(value)) {
124                 throw new IllegalArgumentException("Bad value in PersistableBundle key="
125                         + mMap.keyAt(i) + " value=" + value);
126             }
127         }
128     }
129 
PersistableBundle(Parcel parcelledData, int length)130     /* package */ PersistableBundle(Parcel parcelledData, int length) {
131         super(parcelledData, length);
132         mFlags = FLAG_DEFUSABLE;
133     }
134 
135     /**
136      * Make a PersistableBundle for a single key/value pair.
137      *
138      * @hide
139      */
forPair(String key, String value)140     public static PersistableBundle forPair(String key, String value) {
141         PersistableBundle b = new PersistableBundle(1);
142         b.putString(key, value);
143         return b;
144     }
145 
146     /**
147      * Clones the current PersistableBundle. The internal map is cloned, but the keys and
148      * values to which it refers are copied by reference.
149      */
150     @Override
clone()151     public Object clone() {
152         return new PersistableBundle(this);
153     }
154 
155     /**
156      * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
157      * any existing value for the given key.  Either key or value may be null.
158      *
159      * @param key a String, or null
160      * @param value a Bundle object, or null
161      */
putPersistableBundle(@ullable String key, @Nullable PersistableBundle value)162     public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
163         unparcel();
164         mMap.put(key, value);
165     }
166 
167     /**
168      * Returns the value associated with the given key, or null if
169      * no mapping of the desired type exists for the given key or a null
170      * value is explicitly associated with the key.
171      *
172      * @param key a String, or null
173      * @return a Bundle value, or null
174      */
175     @Nullable
getPersistableBundle(@ullable String key)176     public PersistableBundle getPersistableBundle(@Nullable String key) {
177         unparcel();
178         Object o = mMap.get(key);
179         if (o == null) {
180             return null;
181         }
182         try {
183             return (PersistableBundle) o;
184         } catch (ClassCastException e) {
185             typeWarning(key, o, "Bundle", e);
186             return null;
187         }
188     }
189 
190     public static final Parcelable.Creator<PersistableBundle> CREATOR =
191             new Parcelable.Creator<PersistableBundle>() {
192                 @Override
193                 public PersistableBundle createFromParcel(Parcel in) {
194                     return in.readPersistableBundle();
195                 }
196 
197                 @Override
198                 public PersistableBundle[] newArray(int size) {
199                     return new PersistableBundle[size];
200                 }
201             };
202 
203     /** @hide */
204     @Override
writeUnknownObject(Object v, String name, XmlSerializer out)205     public void writeUnknownObject(Object v, String name, XmlSerializer out)
206             throws XmlPullParserException, IOException {
207         if (v instanceof PersistableBundle) {
208             out.startTag(null, TAG_PERSISTABLEMAP);
209             out.attribute(null, "name", name);
210             ((PersistableBundle) v).saveToXml(out);
211             out.endTag(null, TAG_PERSISTABLEMAP);
212         } else {
213             throw new XmlPullParserException("Unknown Object o=" + v);
214         }
215     }
216 
217     /** @hide */
saveToXml(XmlSerializer out)218     public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
219         unparcel();
220         XmlUtils.writeMapXml(mMap, out, this);
221     }
222 
223     /** @hide */
224     static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
225         @Override
readThisUnknownObjectXml(XmlPullParser in, String tag)226         public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
227                 throws XmlPullParserException, IOException {
228             if (TAG_PERSISTABLEMAP.equals(tag)) {
229                 return restoreFromXml(in);
230             }
231             throw new XmlPullParserException("Unknown tag=" + tag);
232         }
233     }
234 
235     /**
236      * Report the nature of this Parcelable's contents
237      */
238     @Override
describeContents()239     public int describeContents() {
240         return 0;
241     }
242 
243     /**
244      * Writes the PersistableBundle contents to a Parcel, typically in order for
245      * it to be passed through an IBinder connection.
246      * @param parcel The parcel to copy this bundle to.
247      */
248     @Override
writeToParcel(Parcel parcel, int flags)249     public void writeToParcel(Parcel parcel, int flags) {
250         final boolean oldAllowFds = parcel.pushAllowFds(false);
251         try {
252             writeToParcelInner(parcel, flags);
253         } finally {
254             parcel.restoreAllowFds(oldAllowFds);
255         }
256     }
257 
258     /** @hide */
restoreFromXml(XmlPullParser in)259     public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
260             XmlPullParserException {
261         final int outerDepth = in.getDepth();
262         final String startTag = in.getName();
263         final String[] tagName = new String[1];
264         int event;
265         while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
266                 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
267             if (event == XmlPullParser.START_TAG) {
268                 return new PersistableBundle((ArrayMap<String, Object>)
269                         XmlUtils.readThisArrayMapXml(in, startTag, tagName,
270                         new MyReadMapCallback()));
271             }
272         }
273         return EMPTY;
274     }
275 
276     @Override
toString()277     synchronized public String toString() {
278         if (mParcelledData != null) {
279             if (isEmptyParcel()) {
280                 return "PersistableBundle[EMPTY_PARCEL]";
281             } else {
282                 return "PersistableBundle[mParcelledData.dataSize=" +
283                         mParcelledData.dataSize() + "]";
284             }
285         }
286         return "PersistableBundle[" + mMap.toString() + "]";
287     }
288 }
289