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