blob: ea180b216c631dd9300ab225281f828ebc15beaf [file] [log] [blame]
Craig Mautner719e6b12014-04-04 20:29:41 -07001/*
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
17package android.os;
18
Scott Kennedyc6a65dff2015-03-01 17:10:10 -080019import android.annotation.Nullable;
Craig Mautner719e6b12014-04-04 20:29:41 -070020import android.util.ArrayMap;
Craig Mautner21d24a22014-04-23 11:45:37 -070021import com.android.internal.util.XmlUtils;
22import org.xmlpull.v1.XmlPullParser;
23import org.xmlpull.v1.XmlPullParserException;
24import org.xmlpull.v1.XmlSerializer;
Craig Mautner719e6b12014-04-04 20:29:41 -070025
Craig Mautner21d24a22014-04-23 11:45:37 -070026import java.io.IOException;
27import java.util.Iterator;
28import java.util.Map;
Craig Mautner719e6b12014-04-04 20:29:41 -070029import java.util.Set;
30
31/**
32 * A mapping from String values to various types that can be saved to persistent and later
33 * restored.
34 *
35 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -070036public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
37 XmlUtils.WriteMapCallback {
Craig Mautner21d24a22014-04-23 11:45:37 -070038 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
Craig Mautner719e6b12014-04-04 20:29:41 -070039 public static final PersistableBundle EMPTY;
40 static final Parcel EMPTY_PARCEL;
41
42 static {
43 EMPTY = new PersistableBundle();
44 EMPTY.mMap = ArrayMap.EMPTY;
Craig Mautner0a8e160e2014-05-29 10:27:32 -070045 EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
Craig Mautner719e6b12014-04-04 20:29:41 -070046 }
47
Dianne Hackborna83ce1d2015-03-11 15:16:13 -070048 /** @hide */
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
Craig Mautner719e6b12014-04-04 20:29:41 -070058 /**
59 * Constructs a new, empty PersistableBundle.
60 */
61 public PersistableBundle() {
62 super();
63 }
64
65 /**
Craig Mautner719e6b12014-04-04 20:29:41 -070066 * 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 */
71 public PersistableBundle(int capacity) {
72 super(capacity);
73 }
74
75 /**
76 * Constructs a PersistableBundle containing a copy of the mappings from the given
77 * PersistableBundle.
78 *
79 * @param b a PersistableBundle to be copied.
80 */
81 public PersistableBundle(PersistableBundle b) {
82 super(b);
83 }
84
85 /**
Craig Mautner21d24a22014-04-23 11:45:37 -070086 * Constructs a PersistableBundle containing the mappings passed in.
87 *
88 * @param map a Map containing only those items that can be persisted.
89 * @throws IllegalArgumentException if any element of #map cannot be persisted.
90 */
Dianne Hackborna83ce1d2015-03-11 15:16:13 -070091 private PersistableBundle(ArrayMap<String, Object> map) {
Craig Mautner21d24a22014-04-23 11:45:37 -070092 super();
93
94 // First stuff everything in.
95 putAll(map);
96
97 // Now verify each item throwing an exception if there is a violation.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -070098 final int N = mMap.size();
99 for (int i=0; i<N; i++) {
100 Object value = mMap.valueAt(i);
101 if (value instanceof ArrayMap) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700102 // Fix up any Maps by replacing them with PersistableBundles.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700103 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
104 } else if (!isValidType(value)) {
105 throw new IllegalArgumentException("Bad value in PersistableBundle key="
106 + mMap.keyAt(i) + " value=" + value);
Craig Mautner21d24a22014-04-23 11:45:37 -0700107 }
108 }
109 }
110
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700111 /* package */ PersistableBundle(Parcel parcelledData, int length) {
112 super(parcelledData, length);
113 }
114
Craig Mautner21d24a22014-04-23 11:45:37 -0700115 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700116 * Make a PersistableBundle for a single key/value pair.
117 *
118 * @hide
119 */
120 public static PersistableBundle forPair(String key, String value) {
121 PersistableBundle b = new PersistableBundle(1);
122 b.putString(key, value);
123 return b;
124 }
125
126 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700127 * Clones the current PersistableBundle. The internal map is cloned, but the keys and
128 * values to which it refers are copied by reference.
129 */
130 @Override
131 public Object clone() {
132 return new PersistableBundle(this);
133 }
134
135 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700136 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
137 * any existing value for the given key. Either key or value may be null.
138 *
139 * @param key a String, or null
140 * @param value a Bundle object, or null
141 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800142 public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700143 unparcel();
144 mMap.put(key, value);
Craig Mautner719e6b12014-04-04 20:29:41 -0700145 }
146
147 /**
148 * Returns the value associated with the given key, or null if
149 * no mapping of the desired type exists for the given key or a null
150 * value is explicitly associated with the key.
151 *
152 * @param key a String, or null
153 * @return a Bundle value, or null
154 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800155 @Nullable
156 public PersistableBundle getPersistableBundle(@Nullable String key) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700157 unparcel();
158 Object o = mMap.get(key);
159 if (o == null) {
160 return null;
161 }
162 try {
163 return (PersistableBundle) o;
164 } catch (ClassCastException e) {
165 typeWarning(key, o, "Bundle", e);
166 return null;
167 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700168 }
169
170 public static final Parcelable.Creator<PersistableBundle> CREATOR =
171 new Parcelable.Creator<PersistableBundle>() {
172 @Override
173 public PersistableBundle createFromParcel(Parcel in) {
174 return in.readPersistableBundle();
175 }
176
177 @Override
178 public PersistableBundle[] newArray(int size) {
179 return new PersistableBundle[size];
180 }
181 };
182
Craig Mautner21d24a22014-04-23 11:45:37 -0700183 /** @hide */
184 @Override
185 public void writeUnknownObject(Object v, String name, XmlSerializer out)
186 throws XmlPullParserException, IOException {
187 if (v instanceof PersistableBundle) {
188 out.startTag(null, TAG_PERSISTABLEMAP);
189 out.attribute(null, "name", name);
190 ((PersistableBundle) v).saveToXml(out);
191 out.endTag(null, TAG_PERSISTABLEMAP);
192 } else {
193 throw new XmlPullParserException("Unknown Object o=" + v);
194 }
195 }
196
197 /** @hide */
198 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
199 unparcel();
200 XmlUtils.writeMapXml(mMap, out, this);
201 }
202
203 /** @hide */
204 static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
205 @Override
206 public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
207 throws XmlPullParserException, IOException {
208 if (TAG_PERSISTABLEMAP.equals(tag)) {
209 return restoreFromXml(in);
210 }
211 throw new XmlPullParserException("Unknown tag=" + tag);
212 }
213 }
214
215 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700216 * Report the nature of this Parcelable's contents
Craig Mautner21d24a22014-04-23 11:45:37 -0700217 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700218 @Override
219 public int describeContents() {
220 return 0;
221 }
222
223 /**
224 * Writes the PersistableBundle contents to a Parcel, typically in order for
225 * it to be passed through an IBinder connection.
226 * @param parcel The parcel to copy this bundle to.
227 */
228 @Override
229 public void writeToParcel(Parcel parcel, int flags) {
230 final boolean oldAllowFds = parcel.pushAllowFds(false);
231 try {
232 writeToParcelInner(parcel, flags);
233 } finally {
234 parcel.restoreAllowFds(oldAllowFds);
235 }
236 }
237
238 /** @hide */
Craig Mautner21d24a22014-04-23 11:45:37 -0700239 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
240 XmlPullParserException {
241 final int outerDepth = in.getDepth();
242 final String startTag = in.getName();
243 final String[] tagName = new String[1];
244 int event;
245 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
246 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
247 if (event == XmlPullParser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700248 return new PersistableBundle((ArrayMap<String, Object>)
249 XmlUtils.readThisArrayMapXml(in, startTag, tagName,
250 new MyReadMapCallback()));
Craig Mautner21d24a22014-04-23 11:45:37 -0700251 }
252 }
253 return EMPTY;
254 }
255
Craig Mautner719e6b12014-04-04 20:29:41 -0700256 @Override
257 synchronized public String toString() {
258 if (mParcelledData != null) {
259 if (mParcelledData == EMPTY_PARCEL) {
260 return "PersistableBundle[EMPTY_PARCEL]";
261 } else {
262 return "PersistableBundle[mParcelledData.dataSize=" +
263 mParcelledData.dataSize() + "]";
264 }
265 }
266 return "PersistableBundle[" + mMap.toString() + "]";
267 }
268
269}