blob: 3ed5b17452456b3be46648e41984e4bd9403e32d [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
Dianne Hackborn2510b372017-03-03 17:01:38 -080079 * PersistableBundle. Does only a shallow copy of the original PersistableBundle -- see
80 * {@link #deepCopy()} if that is not what you want.
Craig Mautner719e6b12014-04-04 20:29:41 -070081 *
82 * @param b a PersistableBundle to be copied.
Dianne Hackborn2510b372017-03-03 17:01:38 -080083 *
84 * @see #deepCopy()
Craig Mautner719e6b12014-04-04 20:29:41 -070085 */
86 public PersistableBundle(PersistableBundle b) {
87 super(b);
Jeff Sharkeyd136e512016-03-09 22:30:56 -070088 mFlags = b.mFlags;
Craig Mautner719e6b12014-04-04 20:29:41 -070089 }
90
Makoto Onuki6f7362d92016-03-04 13:39:41 -080091
92 /**
Dianne Hackborn2510b372017-03-03 17:01:38 -080093 * Constructs a PersistableBundle from a Bundle. Does only a shallow copy of the Bundle.
Makoto Onuki6f7362d92016-03-04 13:39:41 -080094 *
95 * @param b a Bundle to be copied.
96 *
97 * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
Makoto Onuki55046222016-03-08 10:49:47 -080098 *
99 * @hide
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800100 */
101 public PersistableBundle(Bundle b) {
102 this(b.getMap());
103 }
104
Craig Mautner719e6b12014-04-04 20:29:41 -0700105 /**
Craig Mautner21d24a22014-04-23 11:45:37 -0700106 * 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 */
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700111 private PersistableBundle(ArrayMap<String, Object> map) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700112 super();
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700113 mFlags = FLAG_DEFUSABLE;
Craig Mautner21d24a22014-04-23 11:45:37 -0700114
115 // First stuff everything in.
116 putAll(map);
117
118 // Now verify each item throwing an exception if there is a violation.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700119 final int N = mMap.size();
120 for (int i=0; i<N; i++) {
121 Object value = mMap.valueAt(i);
122 if (value instanceof ArrayMap) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700123 // Fix up any Maps by replacing them with PersistableBundles.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700124 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800125 } else if (value instanceof Bundle) {
126 mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700127 } else if (!isValidType(value)) {
128 throw new IllegalArgumentException("Bad value in PersistableBundle key="
129 + mMap.keyAt(i) + " value=" + value);
Craig Mautner21d24a22014-04-23 11:45:37 -0700130 }
131 }
132 }
133
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700134 /* package */ PersistableBundle(Parcel parcelledData, int length) {
135 super(parcelledData, length);
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700136 mFlags = FLAG_DEFUSABLE;
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700137 }
138
Craig Mautner21d24a22014-04-23 11:45:37 -0700139 /**
Dianne Hackbornba604732016-02-10 17:05:10 -0800140 * Constructs a PersistableBundle without initializing it.
141 */
142 PersistableBundle(boolean doInit) {
143 super(doInit);
144 }
145
146 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700147 * Make a PersistableBundle for a single key/value pair.
148 *
149 * @hide
150 */
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 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700158 * 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
162 public Object clone() {
163 return new PersistableBundle(this);
164 }
165
166 /**
Dianne Hackbornba604732016-02-10 17:05:10 -0800167 * 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 */
Dianne Hackborn2510b372017-03-03 17:01:38 -0800173 public PersistableBundle deepCopy() {
Dianne Hackbornba604732016-02-10 17:05:10 -0800174 PersistableBundle b = new PersistableBundle(false);
175 b.copyInternal(this, true);
176 return b;
177 }
178
179 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700180 * 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 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800186 public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700187 unparcel();
188 mMap.put(key, value);
Craig Mautner719e6b12014-04-04 20:29:41 -0700189 }
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 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800199 @Nullable
200 public PersistableBundle getPersistableBundle(@Nullable String key) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700201 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 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700212 }
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
Craig Mautner21d24a22014-04-23 11:45:37 -0700227 /** @hide */
228 @Override
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 */
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
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 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700260 * Report the nature of this Parcelable's contents
Craig Mautner21d24a22014-04-23 11:45:37 -0700261 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700262 @Override
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
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 */
Craig Mautner21d24a22014-04-23 11:45:37 -0700283 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) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700292 return new PersistableBundle((ArrayMap<String, Object>)
293 XmlUtils.readThisArrayMapXml(in, startTag, tagName,
294 new MyReadMapCallback()));
Craig Mautner21d24a22014-04-23 11:45:37 -0700295 }
296 }
297 return EMPTY;
298 }
299
Craig Mautner719e6b12014-04-04 20:29:41 -0700300 @Override
301 synchronized public String toString() {
302 if (mParcelledData != null) {
Andreas Gampe52764cb2016-04-19 20:46:43 -0700303 if (isEmptyParcel()) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700304 return "PersistableBundle[EMPTY_PARCEL]";
305 } else {
306 return "PersistableBundle[mParcelledData.dataSize=" +
307 mParcelledData.dataSize() + "]";
308 }
309 }
310 return "PersistableBundle[" + mMap.toString() + "]";
311 }
Dianne Hackborna47223f2017-03-30 13:49:13 -0700312
313 /** @hide */
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 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700324}