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