blob: 9b60783737bd7896b0d1036a3aaac2b2c734fad2 [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;
Jeff Sharkeyd136e512016-03-09 22:30:56 -070021
Craig Mautner21d24a22014-04-23 11:45:37 -070022import com.android.internal.util.XmlUtils;
Jeff Sharkeyd136e512016-03-09 22:30:56 -070023
Craig Mautner21d24a22014-04-23 11:45:37 -070024import org.xmlpull.v1.XmlPullParser;
25import org.xmlpull.v1.XmlPullParserException;
26import org.xmlpull.v1.XmlSerializer;
Craig Mautner719e6b12014-04-04 20:29:41 -070027
Craig Mautner21d24a22014-04-23 11:45:37 -070028import java.io.IOException;
Dianne Hackbornba604732016-02-10 17:05:10 -080029import java.util.ArrayList;
Craig Mautner719e6b12014-04-04 20:29:41 -070030
31/**
Jeff Sharkeyd136e512016-03-09 22:30:56 -070032 * 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.
Craig Mautner719e6b12014-04-04 20:29:41 -070035 *
Jeff Sharkeyd136e512016-03-09 22:30:56 -070036 * @see Bundle
Craig Mautner719e6b12014-04-04 20:29:41 -070037 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -070038public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
39 XmlUtils.WriteMapCallback {
Craig Mautner21d24a22014-04-23 11:45:37 -070040 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
Craig Mautner719e6b12014-04-04 20:29:41 -070041 public static final PersistableBundle EMPTY;
Craig Mautner719e6b12014-04-04 20:29:41 -070042
43 static {
44 EMPTY = new PersistableBundle();
45 EMPTY.mMap = ArrayMap.EMPTY;
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();
Jeff Sharkeyd136e512016-03-09 22:30:56 -070063 mFlags = FLAG_DEFUSABLE;
Craig Mautner719e6b12014-04-04 20:29:41 -070064 }
65
66 /**
Craig Mautner719e6b12014-04-04 20:29:41 -070067 * 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 */
72 public PersistableBundle(int capacity) {
73 super(capacity);
Jeff Sharkeyd136e512016-03-09 22:30:56 -070074 mFlags = FLAG_DEFUSABLE;
Craig Mautner719e6b12014-04-04 20:29:41 -070075 }
76
77 /**
78 * Constructs a PersistableBundle containing a copy of the mappings from the given
79 * PersistableBundle.
80 *
81 * @param b a PersistableBundle to be copied.
82 */
83 public PersistableBundle(PersistableBundle b) {
84 super(b);
Jeff Sharkeyd136e512016-03-09 22:30:56 -070085 mFlags = b.mFlags;
Craig Mautner719e6b12014-04-04 20:29:41 -070086 }
87
Makoto Onuki6f7362d92016-03-04 13:39:41 -080088
89 /**
90 * Constructs a PersistableBundle from a Bundle.
91 *
92 * @param b a Bundle to be copied.
93 *
94 * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
Makoto Onuki55046222016-03-08 10:49:47 -080095 *
96 * @hide
Makoto Onuki6f7362d92016-03-04 13:39:41 -080097 */
98 public PersistableBundle(Bundle b) {
99 this(b.getMap());
100 }
101
Craig Mautner719e6b12014-04-04 20:29:41 -0700102 /**
Craig Mautner21d24a22014-04-23 11:45:37 -0700103 * Constructs a PersistableBundle containing the mappings passed in.
104 *
105 * @param map a Map containing only those items that can be persisted.
106 * @throws IllegalArgumentException if any element of #map cannot be persisted.
107 */
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700108 private PersistableBundle(ArrayMap<String, Object> map) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700109 super();
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700110 mFlags = FLAG_DEFUSABLE;
Craig Mautner21d24a22014-04-23 11:45:37 -0700111
112 // First stuff everything in.
113 putAll(map);
114
115 // Now verify each item throwing an exception if there is a violation.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700116 final int N = mMap.size();
117 for (int i=0; i<N; i++) {
118 Object value = mMap.valueAt(i);
119 if (value instanceof ArrayMap) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700120 // Fix up any Maps by replacing them with PersistableBundles.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700121 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800122 } else if (value instanceof Bundle) {
123 mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700124 } else if (!isValidType(value)) {
125 throw new IllegalArgumentException("Bad value in PersistableBundle key="
126 + mMap.keyAt(i) + " value=" + value);
Craig Mautner21d24a22014-04-23 11:45:37 -0700127 }
128 }
129 }
130
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700131 /* package */ PersistableBundle(Parcel parcelledData, int length) {
132 super(parcelledData, length);
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700133 mFlags = FLAG_DEFUSABLE;
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700134 }
135
Craig Mautner21d24a22014-04-23 11:45:37 -0700136 /**
Dianne Hackbornba604732016-02-10 17:05:10 -0800137 * Constructs a PersistableBundle without initializing it.
138 */
139 PersistableBundle(boolean doInit) {
140 super(doInit);
141 }
142
143 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700144 * Make a PersistableBundle for a single key/value pair.
145 *
146 * @hide
147 */
148 public static PersistableBundle forPair(String key, String value) {
149 PersistableBundle b = new PersistableBundle(1);
150 b.putString(key, value);
151 return b;
152 }
153
154 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700155 * Clones the current PersistableBundle. The internal map is cloned, but the keys and
156 * values to which it refers are copied by reference.
157 */
158 @Override
159 public Object clone() {
160 return new PersistableBundle(this);
161 }
162
163 /**
Dianne Hackbornba604732016-02-10 17:05:10 -0800164 * Make a deep copy of the given bundle. Traverses into inner containers and copies
165 * them as well, so they are not shared across bundles. Will traverse in to
166 * {@link Bundle}, {@link PersistableBundle}, {@link ArrayList}, and all types of
167 * primitive arrays. Other types of objects (such as Parcelable or Serializable)
168 * are referenced as-is and not copied in any way.
169 */
170 public PersistableBundle deepcopy() {
171 PersistableBundle b = new PersistableBundle(false);
172 b.copyInternal(this, true);
173 return b;
174 }
175
176 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700177 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
178 * any existing value for the given key. Either key or value may be null.
179 *
180 * @param key a String, or null
181 * @param value a Bundle object, or null
182 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800183 public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700184 unparcel();
185 mMap.put(key, value);
Craig Mautner719e6b12014-04-04 20:29:41 -0700186 }
187
188 /**
189 * Returns the value associated with the given key, or null if
190 * no mapping of the desired type exists for the given key or a null
191 * value is explicitly associated with the key.
192 *
193 * @param key a String, or null
194 * @return a Bundle value, or null
195 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800196 @Nullable
197 public PersistableBundle getPersistableBundle(@Nullable String key) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700198 unparcel();
199 Object o = mMap.get(key);
200 if (o == null) {
201 return null;
202 }
203 try {
204 return (PersistableBundle) o;
205 } catch (ClassCastException e) {
206 typeWarning(key, o, "Bundle", e);
207 return null;
208 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700209 }
210
211 public static final Parcelable.Creator<PersistableBundle> CREATOR =
212 new Parcelable.Creator<PersistableBundle>() {
213 @Override
214 public PersistableBundle createFromParcel(Parcel in) {
215 return in.readPersistableBundle();
216 }
217
218 @Override
219 public PersistableBundle[] newArray(int size) {
220 return new PersistableBundle[size];
221 }
222 };
223
Craig Mautner21d24a22014-04-23 11:45:37 -0700224 /** @hide */
225 @Override
226 public void writeUnknownObject(Object v, String name, XmlSerializer out)
227 throws XmlPullParserException, IOException {
228 if (v instanceof PersistableBundle) {
229 out.startTag(null, TAG_PERSISTABLEMAP);
230 out.attribute(null, "name", name);
231 ((PersistableBundle) v).saveToXml(out);
232 out.endTag(null, TAG_PERSISTABLEMAP);
233 } else {
234 throw new XmlPullParserException("Unknown Object o=" + v);
235 }
236 }
237
238 /** @hide */
239 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
240 unparcel();
241 XmlUtils.writeMapXml(mMap, out, this);
242 }
243
244 /** @hide */
245 static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
246 @Override
247 public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
248 throws XmlPullParserException, IOException {
249 if (TAG_PERSISTABLEMAP.equals(tag)) {
250 return restoreFromXml(in);
251 }
252 throw new XmlPullParserException("Unknown tag=" + tag);
253 }
254 }
255
256 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700257 * Report the nature of this Parcelable's contents
Craig Mautner21d24a22014-04-23 11:45:37 -0700258 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700259 @Override
260 public int describeContents() {
261 return 0;
262 }
263
264 /**
265 * Writes the PersistableBundle contents to a Parcel, typically in order for
266 * it to be passed through an IBinder connection.
267 * @param parcel The parcel to copy this bundle to.
268 */
269 @Override
270 public void writeToParcel(Parcel parcel, int flags) {
271 final boolean oldAllowFds = parcel.pushAllowFds(false);
272 try {
273 writeToParcelInner(parcel, flags);
274 } finally {
275 parcel.restoreAllowFds(oldAllowFds);
276 }
277 }
278
279 /** @hide */
Craig Mautner21d24a22014-04-23 11:45:37 -0700280 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
281 XmlPullParserException {
282 final int outerDepth = in.getDepth();
283 final String startTag = in.getName();
284 final String[] tagName = new String[1];
285 int event;
286 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
287 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
288 if (event == XmlPullParser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700289 return new PersistableBundle((ArrayMap<String, Object>)
290 XmlUtils.readThisArrayMapXml(in, startTag, tagName,
291 new MyReadMapCallback()));
Craig Mautner21d24a22014-04-23 11:45:37 -0700292 }
293 }
294 return EMPTY;
295 }
296
Craig Mautner719e6b12014-04-04 20:29:41 -0700297 @Override
298 synchronized public String toString() {
299 if (mParcelledData != null) {
Andreas Gampe52764cb2016-04-19 20:46:43 -0700300 if (isEmptyParcel()) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700301 return "PersistableBundle[EMPTY_PARCEL]";
302 } else {
303 return "PersistableBundle[mParcelledData.dataSize=" +
304 mParcelledData.dataSize() + "]";
305 }
306 }
307 return "PersistableBundle[" + mMap.toString() + "]";
308 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700309}