blob: f36bb2989c72cec6ceef5ba94ce4be91d4312f56 [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;
Craig Mautner719e6b12014-04-04 20:29:41 -070027
28/**
29 * A mapping from String values to various types that can be saved to persistent and later
30 * restored.
31 *
32 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -070033public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
34 XmlUtils.WriteMapCallback {
Craig Mautner21d24a22014-04-23 11:45:37 -070035 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
Craig Mautner719e6b12014-04-04 20:29:41 -070036 public static final PersistableBundle EMPTY;
37 static final Parcel EMPTY_PARCEL;
38
39 static {
40 EMPTY = new PersistableBundle();
41 EMPTY.mMap = ArrayMap.EMPTY;
Craig Mautner0a8e160e2014-05-29 10:27:32 -070042 EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
Craig Mautner719e6b12014-04-04 20:29:41 -070043 }
44
Dianne Hackborna83ce1d2015-03-11 15:16:13 -070045 /** @hide */
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[]);
53 }
54
Craig Mautner719e6b12014-04-04 20:29:41 -070055 /**
56 * Constructs a new, empty PersistableBundle.
57 */
58 public PersistableBundle() {
59 super();
60 }
61
62 /**
Craig Mautner719e6b12014-04-04 20:29:41 -070063 * Constructs a new, empty PersistableBundle sized to hold the given number of
64 * elements. The PersistableBundle will grow as needed.
65 *
66 * @param capacity the initial capacity of the PersistableBundle
67 */
68 public PersistableBundle(int capacity) {
69 super(capacity);
70 }
71
72 /**
73 * Constructs a PersistableBundle containing a copy of the mappings from the given
74 * PersistableBundle.
75 *
76 * @param b a PersistableBundle to be copied.
77 */
78 public PersistableBundle(PersistableBundle b) {
79 super(b);
80 }
81
Makoto Onuki6f7362d92016-03-04 13:39:41 -080082
83 /**
84 * Constructs a PersistableBundle from a Bundle.
85 *
86 * @param b a Bundle to be copied.
87 *
88 * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
89 */
90 public PersistableBundle(Bundle b) {
91 this(b.getMap());
92 }
93
Craig Mautner719e6b12014-04-04 20:29:41 -070094 /**
Craig Mautner21d24a22014-04-23 11:45:37 -070095 * Constructs a PersistableBundle containing the mappings passed in.
96 *
97 * @param map a Map containing only those items that can be persisted.
98 * @throws IllegalArgumentException if any element of #map cannot be persisted.
99 */
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700100 private PersistableBundle(ArrayMap<String, Object> map) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700101 super();
102
103 // First stuff everything in.
104 putAll(map);
105
106 // Now verify each item throwing an exception if there is a violation.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700107 final int N = mMap.size();
108 for (int i=0; i<N; i++) {
109 Object value = mMap.valueAt(i);
110 if (value instanceof ArrayMap) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700111 // Fix up any Maps by replacing them with PersistableBundles.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700112 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800113 } else if (value instanceof Bundle) {
114 mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700115 } else if (!isValidType(value)) {
116 throw new IllegalArgumentException("Bad value in PersistableBundle key="
117 + mMap.keyAt(i) + " value=" + value);
Craig Mautner21d24a22014-04-23 11:45:37 -0700118 }
119 }
120 }
121
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700122 /* package */ PersistableBundle(Parcel parcelledData, int length) {
123 super(parcelledData, length);
124 }
125
Craig Mautner21d24a22014-04-23 11:45:37 -0700126 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700127 * Make a PersistableBundle for a single key/value pair.
128 *
129 * @hide
130 */
131 public static PersistableBundle forPair(String key, String value) {
132 PersistableBundle b = new PersistableBundle(1);
133 b.putString(key, value);
134 return b;
135 }
136
137 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700138 * Clones the current PersistableBundle. The internal map is cloned, but the keys and
139 * values to which it refers are copied by reference.
140 */
141 @Override
142 public Object clone() {
143 return new PersistableBundle(this);
144 }
145
146 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700147 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
148 * any existing value for the given key. Either key or value may be null.
149 *
150 * @param key a String, or null
151 * @param value a Bundle object, or null
152 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800153 public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700154 unparcel();
155 mMap.put(key, value);
Craig Mautner719e6b12014-04-04 20:29:41 -0700156 }
157
158 /**
159 * Returns the value associated with the given key, or null if
160 * no mapping of the desired type exists for the given key or a null
161 * value is explicitly associated with the key.
162 *
163 * @param key a String, or null
164 * @return a Bundle value, or null
165 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800166 @Nullable
167 public PersistableBundle getPersistableBundle(@Nullable String key) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700168 unparcel();
169 Object o = mMap.get(key);
170 if (o == null) {
171 return null;
172 }
173 try {
174 return (PersistableBundle) o;
175 } catch (ClassCastException e) {
176 typeWarning(key, o, "Bundle", e);
177 return null;
178 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700179 }
180
181 public static final Parcelable.Creator<PersistableBundle> CREATOR =
182 new Parcelable.Creator<PersistableBundle>() {
183 @Override
184 public PersistableBundle createFromParcel(Parcel in) {
185 return in.readPersistableBundle();
186 }
187
188 @Override
189 public PersistableBundle[] newArray(int size) {
190 return new PersistableBundle[size];
191 }
192 };
193
Craig Mautner21d24a22014-04-23 11:45:37 -0700194 /** @hide */
195 @Override
196 public void writeUnknownObject(Object v, String name, XmlSerializer out)
197 throws XmlPullParserException, IOException {
198 if (v instanceof PersistableBundle) {
199 out.startTag(null, TAG_PERSISTABLEMAP);
200 out.attribute(null, "name", name);
201 ((PersistableBundle) v).saveToXml(out);
202 out.endTag(null, TAG_PERSISTABLEMAP);
203 } else {
204 throw new XmlPullParserException("Unknown Object o=" + v);
205 }
206 }
207
208 /** @hide */
209 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
210 unparcel();
211 XmlUtils.writeMapXml(mMap, out, this);
212 }
213
214 /** @hide */
215 static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
216 @Override
217 public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
218 throws XmlPullParserException, IOException {
219 if (TAG_PERSISTABLEMAP.equals(tag)) {
220 return restoreFromXml(in);
221 }
222 throw new XmlPullParserException("Unknown tag=" + tag);
223 }
224 }
225
226 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700227 * Report the nature of this Parcelable's contents
Craig Mautner21d24a22014-04-23 11:45:37 -0700228 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700229 @Override
230 public int describeContents() {
231 return 0;
232 }
233
234 /**
235 * Writes the PersistableBundle contents to a Parcel, typically in order for
236 * it to be passed through an IBinder connection.
237 * @param parcel The parcel to copy this bundle to.
238 */
239 @Override
240 public void writeToParcel(Parcel parcel, int flags) {
241 final boolean oldAllowFds = parcel.pushAllowFds(false);
242 try {
243 writeToParcelInner(parcel, flags);
244 } finally {
245 parcel.restoreAllowFds(oldAllowFds);
246 }
247 }
248
249 /** @hide */
Craig Mautner21d24a22014-04-23 11:45:37 -0700250 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
251 XmlPullParserException {
252 final int outerDepth = in.getDepth();
253 final String startTag = in.getName();
254 final String[] tagName = new String[1];
255 int event;
256 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
257 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
258 if (event == XmlPullParser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700259 return new PersistableBundle((ArrayMap<String, Object>)
260 XmlUtils.readThisArrayMapXml(in, startTag, tagName,
261 new MyReadMapCallback()));
Craig Mautner21d24a22014-04-23 11:45:37 -0700262 }
263 }
264 return EMPTY;
265 }
266
Craig Mautner719e6b12014-04-04 20:29:41 -0700267 @Override
268 synchronized public String toString() {
269 if (mParcelledData != null) {
270 if (mParcelledData == EMPTY_PARCEL) {
271 return "PersistableBundle[EMPTY_PARCEL]";
272 } else {
273 return "PersistableBundle[mParcelledData.dataSize=" +
274 mParcelledData.dataSize() + "]";
275 }
276 }
277 return "PersistableBundle[" + mMap.toString() + "]";
278 }
279
280}