PersistableBundle.java revision 0a8e160eb56f3b8f504b37349a79ec4edb7e5039
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 17package android.os; 18 19import android.util.ArrayMap; 20import com.android.internal.util.XmlUtils; 21import org.xmlpull.v1.XmlPullParser; 22import org.xmlpull.v1.XmlPullParserException; 23import org.xmlpull.v1.XmlSerializer; 24 25import java.io.IOException; 26import java.util.Iterator; 27import java.util.Map; 28import java.util.Set; 29 30/** 31 * A mapping from String values to various types that can be saved to persistent and later 32 * restored. 33 * 34 */ 35public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable, 36 XmlUtils.WriteMapCallback { 37 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map"; 38 public static final PersistableBundle EMPTY; 39 static final Parcel EMPTY_PARCEL; 40 41 static { 42 EMPTY = new PersistableBundle(); 43 EMPTY.mMap = ArrayMap.EMPTY; 44 EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL; 45 } 46 47 /** 48 * Constructs a new, empty PersistableBundle. 49 */ 50 public PersistableBundle() { 51 super(); 52 } 53 54 /** 55 * Constructs a new, empty PersistableBundle sized to hold the given number of 56 * elements. The PersistableBundle will grow as needed. 57 * 58 * @param capacity the initial capacity of the PersistableBundle 59 */ 60 public PersistableBundle(int capacity) { 61 super(capacity); 62 } 63 64 /** 65 * Constructs a PersistableBundle containing a copy of the mappings from the given 66 * PersistableBundle. 67 * 68 * @param b a PersistableBundle to be copied. 69 */ 70 public PersistableBundle(PersistableBundle b) { 71 super(b); 72 } 73 74 /** 75 * Constructs a PersistableBundle containing the mappings passed in. 76 * 77 * @param map a Map containing only those items that can be persisted. 78 * @throws IllegalArgumentException if any element of #map cannot be persisted. 79 */ 80 private PersistableBundle(Map<String, Object> map) { 81 super(); 82 83 // First stuff everything in. 84 putAll(map); 85 86 // Now verify each item throwing an exception if there is a violation. 87 Set<String> keys = map.keySet(); 88 Iterator<String> iterator = keys.iterator(); 89 while (iterator.hasNext()) { 90 String key = iterator.next(); 91 Object value = map.get(key); 92 if (value instanceof Map) { 93 // Fix up any Maps by replacing them with PersistableBundles. 94 putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value)); 95 } else if (!(value instanceof Integer) && !(value instanceof Long) && 96 !(value instanceof Double) && !(value instanceof String) && 97 !(value instanceof int[]) && !(value instanceof long[]) && 98 !(value instanceof double[]) && !(value instanceof String[]) && 99 !(value instanceof PersistableBundle) && (value != null)) { 100 throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key + 101 " value=" + value); 102 } 103 } 104 } 105 106 /* package */ PersistableBundle(Parcel parcelledData, int length) { 107 super(parcelledData, length); 108 } 109 110 /** 111 * Make a PersistableBundle for a single key/value pair. 112 * 113 * @hide 114 */ 115 public static PersistableBundle forPair(String key, String value) { 116 PersistableBundle b = new PersistableBundle(1); 117 b.putString(key, value); 118 return b; 119 } 120 121 /** 122 * Clones the current PersistableBundle. The internal map is cloned, but the keys and 123 * values to which it refers are copied by reference. 124 */ 125 @Override 126 public Object clone() { 127 return new PersistableBundle(this); 128 } 129 130 /** 131 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing 132 * any existing value for the given key. Either key or value may be null. 133 * 134 * @param key a String, or null 135 * @param value a Bundle object, or null 136 */ 137 public void putPersistableBundle(String key, PersistableBundle value) { 138 unparcel(); 139 mMap.put(key, value); 140 } 141 142 /** 143 * Returns the value associated with the given key, or null if 144 * no mapping of the desired type exists for the given key or a null 145 * value is explicitly associated with the key. 146 * 147 * @param key a String, or null 148 * @return a Bundle value, or null 149 */ 150 public PersistableBundle getPersistableBundle(String key) { 151 unparcel(); 152 Object o = mMap.get(key); 153 if (o == null) { 154 return null; 155 } 156 try { 157 return (PersistableBundle) o; 158 } catch (ClassCastException e) { 159 typeWarning(key, o, "Bundle", e); 160 return null; 161 } 162 } 163 164 public static final Parcelable.Creator<PersistableBundle> CREATOR = 165 new Parcelable.Creator<PersistableBundle>() { 166 @Override 167 public PersistableBundle createFromParcel(Parcel in) { 168 return in.readPersistableBundle(); 169 } 170 171 @Override 172 public PersistableBundle[] newArray(int size) { 173 return new PersistableBundle[size]; 174 } 175 }; 176 177 /** @hide */ 178 @Override 179 public void writeUnknownObject(Object v, String name, XmlSerializer out) 180 throws XmlPullParserException, IOException { 181 if (v instanceof PersistableBundle) { 182 out.startTag(null, TAG_PERSISTABLEMAP); 183 out.attribute(null, "name", name); 184 ((PersistableBundle) v).saveToXml(out); 185 out.endTag(null, TAG_PERSISTABLEMAP); 186 } else { 187 throw new XmlPullParserException("Unknown Object o=" + v); 188 } 189 } 190 191 /** @hide */ 192 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { 193 unparcel(); 194 XmlUtils.writeMapXml(mMap, out, this); 195 } 196 197 /** @hide */ 198 static class MyReadMapCallback implements XmlUtils.ReadMapCallback { 199 @Override 200 public Object readThisUnknownObjectXml(XmlPullParser in, String tag) 201 throws XmlPullParserException, IOException { 202 if (TAG_PERSISTABLEMAP.equals(tag)) { 203 return restoreFromXml(in); 204 } 205 throw new XmlPullParserException("Unknown tag=" + tag); 206 } 207 } 208 209 /** 210 * Report the nature of this Parcelable's contents 211 */ 212 @Override 213 public int describeContents() { 214 return 0; 215 } 216 217 /** 218 * Writes the PersistableBundle contents to a Parcel, typically in order for 219 * it to be passed through an IBinder connection. 220 * @param parcel The parcel to copy this bundle to. 221 */ 222 @Override 223 public void writeToParcel(Parcel parcel, int flags) { 224 final boolean oldAllowFds = parcel.pushAllowFds(false); 225 try { 226 writeToParcelInner(parcel, flags); 227 } finally { 228 parcel.restoreAllowFds(oldAllowFds); 229 } 230 } 231 232 /** @hide */ 233 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException, 234 XmlPullParserException { 235 final int outerDepth = in.getDepth(); 236 final String startTag = in.getName(); 237 final String[] tagName = new String[1]; 238 int event; 239 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && 240 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 241 if (event == XmlPullParser.START_TAG) { 242 return new PersistableBundle((Map<String, Object>) 243 XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback())); 244 } 245 } 246 return EMPTY; 247 } 248 249 @Override 250 synchronized public String toString() { 251 if (mParcelledData != null) { 252 if (mParcelledData == EMPTY_PARCEL) { 253 return "PersistableBundle[EMPTY_PARCEL]"; 254 } else { 255 return "PersistableBundle[mParcelledData.dataSize=" + 256 mParcelledData.dataSize() + "]"; 257 } 258 } 259 return "PersistableBundle[" + mMap.toString() + "]"; 260 } 261 262} 263