PersistableBundle.java revision 73bdf9761be2abdd85efc5fce165f3fa80fcfa65
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 !(value instanceof Boolean) && !(value instanceof boolean[])) { 101 throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key + 102 " value=" + value); 103 } 104 } 105 } 106 107 /* package */ PersistableBundle(Parcel parcelledData, int length) { 108 super(parcelledData, length); 109 } 110 111 /** 112 * Make a PersistableBundle for a single key/value pair. 113 * 114 * @hide 115 */ 116 public static PersistableBundle forPair(String key, String value) { 117 PersistableBundle b = new PersistableBundle(1); 118 b.putString(key, value); 119 return b; 120 } 121 122 /** 123 * Clones the current PersistableBundle. The internal map is cloned, but the keys and 124 * values to which it refers are copied by reference. 125 */ 126 @Override 127 public Object clone() { 128 return new PersistableBundle(this); 129 } 130 131 /** 132 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing 133 * any existing value for the given key. Either key or value may be null. 134 * 135 * @param key a String, or null 136 * @param value a Bundle object, or null 137 */ 138 public void putPersistableBundle(String key, PersistableBundle value) { 139 unparcel(); 140 mMap.put(key, value); 141 } 142 143 /** 144 * Returns the value associated with the given key, or null if 145 * no mapping of the desired type exists for the given key or a null 146 * value is explicitly associated with the key. 147 * 148 * @param key a String, or null 149 * @return a Bundle value, or null 150 */ 151 public PersistableBundle getPersistableBundle(String key) { 152 unparcel(); 153 Object o = mMap.get(key); 154 if (o == null) { 155 return null; 156 } 157 try { 158 return (PersistableBundle) o; 159 } catch (ClassCastException e) { 160 typeWarning(key, o, "Bundle", e); 161 return null; 162 } 163 } 164 165 public static final Parcelable.Creator<PersistableBundle> CREATOR = 166 new Parcelable.Creator<PersistableBundle>() { 167 @Override 168 public PersistableBundle createFromParcel(Parcel in) { 169 return in.readPersistableBundle(); 170 } 171 172 @Override 173 public PersistableBundle[] newArray(int size) { 174 return new PersistableBundle[size]; 175 } 176 }; 177 178 /** @hide */ 179 @Override 180 public void writeUnknownObject(Object v, String name, XmlSerializer out) 181 throws XmlPullParserException, IOException { 182 if (v instanceof PersistableBundle) { 183 out.startTag(null, TAG_PERSISTABLEMAP); 184 out.attribute(null, "name", name); 185 ((PersistableBundle) v).saveToXml(out); 186 out.endTag(null, TAG_PERSISTABLEMAP); 187 } else { 188 throw new XmlPullParserException("Unknown Object o=" + v); 189 } 190 } 191 192 /** @hide */ 193 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { 194 unparcel(); 195 XmlUtils.writeMapXml(mMap, out, this); 196 } 197 198 /** @hide */ 199 static class MyReadMapCallback implements XmlUtils.ReadMapCallback { 200 @Override 201 public Object readThisUnknownObjectXml(XmlPullParser in, String tag) 202 throws XmlPullParserException, IOException { 203 if (TAG_PERSISTABLEMAP.equals(tag)) { 204 return restoreFromXml(in); 205 } 206 throw new XmlPullParserException("Unknown tag=" + tag); 207 } 208 } 209 210 /** 211 * Report the nature of this Parcelable's contents 212 */ 213 @Override 214 public int describeContents() { 215 return 0; 216 } 217 218 /** 219 * Writes the PersistableBundle contents to a Parcel, typically in order for 220 * it to be passed through an IBinder connection. 221 * @param parcel The parcel to copy this bundle to. 222 */ 223 @Override 224 public void writeToParcel(Parcel parcel, int flags) { 225 final boolean oldAllowFds = parcel.pushAllowFds(false); 226 try { 227 writeToParcelInner(parcel, flags); 228 } finally { 229 parcel.restoreAllowFds(oldAllowFds); 230 } 231 } 232 233 /** @hide */ 234 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException, 235 XmlPullParserException { 236 final int outerDepth = in.getDepth(); 237 final String startTag = in.getName(); 238 final String[] tagName = new String[1]; 239 int event; 240 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && 241 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 242 if (event == XmlPullParser.START_TAG) { 243 return new PersistableBundle((Map<String, Object>) 244 XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback())); 245 } 246 } 247 return EMPTY; 248 } 249 250 @Override 251 synchronized public String toString() { 252 if (mParcelledData != null) { 253 if (mParcelledData == EMPTY_PARCEL) { 254 return "PersistableBundle[EMPTY_PARCEL]"; 255 } else { 256 return "PersistableBundle[mParcelledData.dataSize=" + 257 mParcelledData.dataSize() + "]"; 258 } 259 } 260 return "PersistableBundle[" + mMap.toString() + "]"; 261 } 262 263} 264