OSDN Git Service

5872f7438904e0a11a0f7d9564ca6f05e8486b99
[android-x86/frameworks-base.git] / core / java / android / os / PersistableBundle.java
1 /*
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
17 package android.os;
18
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;
25
26 import java.io.IOException;
27
28 /**
29  * A mapping from String values to various types that can be saved to persistent and later
30  * restored.
31  *
32  */
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;
38
39     static {
40         EMPTY = new PersistableBundle();
41         EMPTY.mMap = ArrayMap.EMPTY;
42         EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
43     }
44
45     /** @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
55     /**
56      * Constructs a new, empty PersistableBundle.
57      */
58     public PersistableBundle() {
59         super();
60     }
61
62     /**
63      * 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
82
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      * @hide
91      */
92     public PersistableBundle(Bundle b) {
93         this(b.getMap());
94     }
95
96     /**
97      * Constructs a PersistableBundle containing the mappings passed in.
98      *
99      * @param map a Map containing only those items that can be persisted.
100      * @throws IllegalArgumentException if any element of #map cannot be persisted.
101      */
102     private PersistableBundle(ArrayMap<String, Object> map) {
103         super();
104
105         // First stuff everything in.
106         putAll(map);
107
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);
120             }
121         }
122     }
123
124     /* package */ PersistableBundle(Parcel parcelledData, int length) {
125         super(parcelledData, length);
126     }
127
128     /**
129      * Make a PersistableBundle for a single key/value pair.
130      *
131      * @hide
132      */
133     public static PersistableBundle forPair(String key, String value) {
134         PersistableBundle b = new PersistableBundle(1);
135         b.putString(key, value);
136         return b;
137     }
138
139     /**
140      * Clones the current PersistableBundle. The internal map is cloned, but the keys and
141      * values to which it refers are copied by reference.
142      */
143     @Override
144     public Object clone() {
145         return new PersistableBundle(this);
146     }
147
148     /**
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.
151      *
152      * @param key a String, or null
153      * @param value a Bundle object, or null
154      */
155     public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
156         unparcel();
157         mMap.put(key, value);
158     }
159
160     /**
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.
164      *
165      * @param key a String, or null
166      * @return a Bundle value, or null
167      */
168     @Nullable
169     public PersistableBundle getPersistableBundle(@Nullable String key) {
170         unparcel();
171         Object o = mMap.get(key);
172         if (o == null) {
173             return null;
174         }
175         try {
176             return (PersistableBundle) o;
177         } catch (ClassCastException e) {
178             typeWarning(key, o, "Bundle", e);
179             return null;
180         }
181     }
182
183     public static final Parcelable.Creator<PersistableBundle> CREATOR =
184             new Parcelable.Creator<PersistableBundle>() {
185                 @Override
186                 public PersistableBundle createFromParcel(Parcel in) {
187                     return in.readPersistableBundle();
188                 }
189
190                 @Override
191                 public PersistableBundle[] newArray(int size) {
192                     return new PersistableBundle[size];
193                 }
194             };
195
196     /** @hide */
197     @Override
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);
205         } else {
206             throw new XmlPullParserException("Unknown Object o=" + v);
207         }
208     }
209
210     /** @hide */
211     public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
212         unparcel();
213         XmlUtils.writeMapXml(mMap, out, this);
214     }
215
216     /** @hide */
217     static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
218         @Override
219         public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
220                 throws XmlPullParserException, IOException {
221             if (TAG_PERSISTABLEMAP.equals(tag)) {
222                 return restoreFromXml(in);
223             }
224             throw new XmlPullParserException("Unknown tag=" + tag);
225         }
226     }
227
228     /**
229      * Report the nature of this Parcelable's contents
230      */
231     @Override
232     public int describeContents() {
233         return 0;
234     }
235
236     /**
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.
240      */
241     @Override
242     public void writeToParcel(Parcel parcel, int flags) {
243         final boolean oldAllowFds = parcel.pushAllowFds(false);
244         try {
245             writeToParcelInner(parcel, flags);
246         } finally {
247             parcel.restoreAllowFds(oldAllowFds);
248         }
249     }
250
251     /** @hide */
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];
257         int event;
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()));
264             }
265         }
266         return EMPTY;
267     }
268
269     @Override
270     synchronized public String toString() {
271         if (mParcelledData != null) {
272             if (mParcelledData == EMPTY_PARCEL) {
273                 return "PersistableBundle[EMPTY_PARCEL]";
274             } else {
275                 return "PersistableBundle[mParcelledData.dataSize=" +
276                         mParcelledData.dataSize() + "]";
277             }
278         }
279         return "PersistableBundle[" + mMap.toString() + "]";
280     }
281
282 }