2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import android.annotation.Nullable;
20 import android.util.ArrayMap;
21 import com.android.internal.util.XmlUtils;
22 import org.xmlpull.v1.XmlPullParser;
23 import org.xmlpull.v1.XmlPullParserException;
24 import org.xmlpull.v1.XmlSerializer;
26 import java.io.IOException;
29 * A mapping from String values to various types that can be saved to persistent and later
33 public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
34 XmlUtils.WriteMapCallback {
35 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
36 public static final PersistableBundle EMPTY;
37 static final Parcel EMPTY_PARCEL;
40 EMPTY = new PersistableBundle();
41 EMPTY.mMap = ArrayMap.EMPTY;
42 EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
46 public static boolean isValidType(Object value) {
47 return (value instanceof Integer) || (value instanceof Long) ||
48 (value instanceof Double) || (value instanceof String) ||
49 (value instanceof int[]) || (value instanceof long[]) ||
50 (value instanceof double[]) || (value instanceof String[]) ||
51 (value instanceof PersistableBundle) || (value == null) ||
52 (value instanceof Boolean) || (value instanceof boolean[]);
56 * Constructs a new, empty PersistableBundle.
58 public PersistableBundle() {
63 * Constructs a new, empty PersistableBundle sized to hold the given number of
64 * elements. The PersistableBundle will grow as needed.
66 * @param capacity the initial capacity of the PersistableBundle
68 public PersistableBundle(int capacity) {
73 * Constructs a PersistableBundle containing a copy of the mappings from the given
76 * @param b a PersistableBundle to be copied.
78 public PersistableBundle(PersistableBundle b) {
84 * Constructs a PersistableBundle from a Bundle.
86 * @param b a Bundle to be copied.
88 * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
92 public PersistableBundle(Bundle b) {
97 * Constructs a PersistableBundle containing the mappings passed in.
99 * @param map a Map containing only those items that can be persisted.
100 * @throws IllegalArgumentException if any element of #map cannot be persisted.
102 private PersistableBundle(ArrayMap<String, Object> map) {
105 // First stuff everything in.
108 // Now verify each item throwing an exception if there is a violation.
109 final int N = mMap.size();
110 for (int i=0; i<N; i++) {
111 Object value = mMap.valueAt(i);
112 if (value instanceof ArrayMap) {
113 // Fix up any Maps by replacing them with PersistableBundles.
114 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
115 } else if (value instanceof Bundle) {
116 mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
117 } else if (!isValidType(value)) {
118 throw new IllegalArgumentException("Bad value in PersistableBundle key="
119 + mMap.keyAt(i) + " value=" + value);
124 /* package */ PersistableBundle(Parcel parcelledData, int length) {
125 super(parcelledData, length);
129 * Make a PersistableBundle for a single key/value pair.
133 public static PersistableBundle forPair(String key, String value) {
134 PersistableBundle b = new PersistableBundle(1);
135 b.putString(key, value);
140 * Clones the current PersistableBundle. The internal map is cloned, but the keys and
141 * values to which it refers are copied by reference.
144 public Object clone() {
145 return new PersistableBundle(this);
149 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
150 * any existing value for the given key. Either key or value may be null.
152 * @param key a String, or null
153 * @param value a Bundle object, or null
155 public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
157 mMap.put(key, value);
161 * Returns the value associated with the given key, or null if
162 * no mapping of the desired type exists for the given key or a null
163 * value is explicitly associated with the key.
165 * @param key a String, or null
166 * @return a Bundle value, or null
169 public PersistableBundle getPersistableBundle(@Nullable String key) {
171 Object o = mMap.get(key);
176 return (PersistableBundle) o;
177 } catch (ClassCastException e) {
178 typeWarning(key, o, "Bundle", e);
183 public static final Parcelable.Creator<PersistableBundle> CREATOR =
184 new Parcelable.Creator<PersistableBundle>() {
186 public PersistableBundle createFromParcel(Parcel in) {
187 return in.readPersistableBundle();
191 public PersistableBundle[] newArray(int size) {
192 return new PersistableBundle[size];
198 public void writeUnknownObject(Object v, String name, XmlSerializer out)
199 throws XmlPullParserException, IOException {
200 if (v instanceof PersistableBundle) {
201 out.startTag(null, TAG_PERSISTABLEMAP);
202 out.attribute(null, "name", name);
203 ((PersistableBundle) v).saveToXml(out);
204 out.endTag(null, TAG_PERSISTABLEMAP);
206 throw new XmlPullParserException("Unknown Object o=" + v);
211 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
213 XmlUtils.writeMapXml(mMap, out, this);
217 static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
219 public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
220 throws XmlPullParserException, IOException {
221 if (TAG_PERSISTABLEMAP.equals(tag)) {
222 return restoreFromXml(in);
224 throw new XmlPullParserException("Unknown tag=" + tag);
229 * Report the nature of this Parcelable's contents
232 public int describeContents() {
237 * Writes the PersistableBundle contents to a Parcel, typically in order for
238 * it to be passed through an IBinder connection.
239 * @param parcel The parcel to copy this bundle to.
242 public void writeToParcel(Parcel parcel, int flags) {
243 final boolean oldAllowFds = parcel.pushAllowFds(false);
245 writeToParcelInner(parcel, flags);
247 parcel.restoreAllowFds(oldAllowFds);
252 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
253 XmlPullParserException {
254 final int outerDepth = in.getDepth();
255 final String startTag = in.getName();
256 final String[] tagName = new String[1];
258 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
259 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
260 if (event == XmlPullParser.START_TAG) {
261 return new PersistableBundle((ArrayMap<String, Object>)
262 XmlUtils.readThisArrayMapXml(in, startTag, tagName,
263 new MyReadMapCallback()));
270 synchronized public String toString() {
271 if (mParcelledData != null) {
272 if (mParcelledData == EMPTY_PARCEL) {
273 return "PersistableBundle[EMPTY_PARCEL]";
275 return "PersistableBundle[mParcelledData.dataSize=" +
276 mParcelledData.dataSize() + "]";
279 return "PersistableBundle[" + mMap.toString() + "]";