1 /* 2 * Copyright 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 package com.android.managedprovisioning; 17 18 import android.accounts.Account; 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.PersistableBundle; 23 import android.util.Xml; 24 25 import com.android.internal.util.FastXmlSerializer; 26 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 import java.nio.charset.StandardCharsets; 32 import java.util.Set; 33 34 import org.xmlpull.v1.XmlPullParser; 35 import org.xmlpull.v1.XmlPullParserException; 36 import org.xmlpull.v1.XmlSerializer; 37 38 /** 39 * This class is used to store an intent to an xml file, and then restore it. 40 * Can only store: 41 * - the action 42 * - the component name 43 * - extras of type string, Integer, Long, Boolean, ComponentName, PersistableBundle, Account 44 */ 45 public class IntentStore { 46 47 private static final String TAG_INTENT_STORE = "intent-store"; 48 49 private static final String TAG_ACTION = "action"; 50 private static final String TAG_COMPONENT_NAME = "component-name"; 51 52 private static final String TAG_EXTRAS = "extras"; 53 private static final String TAG_STRING = "string"; 54 private static final String TAG_INTEGER = "integer"; 55 private static final String TAG_LONG = "long"; 56 private static final String TAG_BOOLEAN = "boolean"; 57 private static final String TAG_ACCOUNT = "account"; 58 private static final String TAG_PERSISTABLE_BUNDLE = "persistable-bundle"; 59 60 private static final String ATTR_VALUE = "value"; 61 private static final String ATTR_NAME = "name"; 62 private static final String ATTR_ACCOUNT_NAME = "account-name"; 63 private static final String ATTR_ACCOUNT_TYPE = "account-type"; 64 65 private final Context mContext; 66 private final String mName; 67 68 private static final Object STATIC_LOCK = new Object(); 69 IntentStore(Context context, String name)70 public IntentStore(Context context, String name) { 71 mName = name; 72 mContext = context; 73 } 74 save(Intent intent)75 public boolean save(Intent intent) { 76 synchronized(STATIC_LOCK) { 77 File file = getFile(); 78 if (file.exists()) { 79 ProvisionLogger.loge("Cannot save to the intent store because it already contains" 80 + " an intent"); 81 return false; 82 } 83 try (FileOutputStream stream = new FileOutputStream(file)){ 84 XmlSerializer serializer = new FastXmlSerializer(); 85 serializer.setOutput(stream, StandardCharsets.UTF_8.name()); 86 serializer.startDocument(null, true); 87 serializer.startTag(null, TAG_INTENT_STORE); 88 writeIntent(intent, serializer); 89 serializer.endTag(null, TAG_INTENT_STORE); 90 serializer.endDocument(); 91 return true; 92 } catch (IOException | XmlPullParserException e) { 93 ProvisionLogger.loge("Caught exception while trying to save an intent to the" 94 + " intentStore", e); 95 file.delete(); 96 return false; 97 } 98 } 99 } 100 writeIntent(Intent intent, XmlSerializer serializer)101 private void writeIntent(Intent intent, XmlSerializer serializer) 102 throws IOException, XmlPullParserException { 103 if (intent.getAction() != null) { 104 writeTag(serializer, TAG_ACTION, intent.getAction()); 105 } 106 if (intent.getComponent() != null) { 107 writeTag(serializer, TAG_COMPONENT_NAME, 108 intent.getComponent().flattenToString()); 109 } 110 if (intent.getExtras() != null) { 111 writeExtras(intent, serializer); 112 } 113 } 114 writeExtras(Intent intent, XmlSerializer serializer)115 private void writeExtras(Intent intent, XmlSerializer serializer) 116 throws IOException, XmlPullParserException { 117 serializer.startTag(null, TAG_EXTRAS); 118 for (String key : intent.getExtras().keySet()) { 119 Object o = intent.getExtra(key); 120 if (o instanceof String) { 121 writeTag(serializer, TAG_STRING, key, (String) o); 122 } else if (o instanceof Integer) { 123 writeTag(serializer, TAG_INTEGER, key, o.toString()); 124 } else if (o instanceof Long) { 125 writeTag(serializer, TAG_LONG, key, o.toString()); 126 } else if (o instanceof Boolean) { 127 writeTag(serializer, TAG_BOOLEAN, key, o.toString()); 128 } else if (o instanceof ComponentName) { 129 writeTag(serializer, TAG_COMPONENT_NAME, key, 130 ((ComponentName) o).flattenToString()); 131 } else if (o instanceof Account) { 132 Account account = (Account) o; 133 serializer.startTag(null, TAG_ACCOUNT); 134 serializer.attribute(null, ATTR_NAME, key); 135 serializer.attribute(null, ATTR_ACCOUNT_NAME, account.name); 136 serializer.attribute(null, ATTR_ACCOUNT_TYPE, account.type); 137 serializer.endTag(null, TAG_ACCOUNT); 138 } else if (o instanceof PersistableBundle) { 139 serializer.startTag(null, TAG_PERSISTABLE_BUNDLE); 140 serializer.attribute(null, ATTR_NAME, key); 141 ((PersistableBundle) o).saveToXml(serializer); 142 serializer.endTag(null, TAG_PERSISTABLE_BUNDLE); 143 } else if (o != null) { 144 ProvisionLogger.loge("extra for key " + key + " cannot be save in the intent" 145 + " store: " + o, new RuntimeException()); 146 } 147 } 148 serializer.endTag(null, TAG_EXTRAS); 149 } 150 load()151 public Intent load() { 152 File file = getFile(); 153 if (!file.exists()) { 154 return null; 155 } 156 synchronized(STATIC_LOCK) { 157 try (FileInputStream stream = new FileInputStream(file)) { 158 XmlPullParser parser = Xml.newPullParser(); 159 parser.setInput(stream, null); 160 Intent result = parseIntent(parser); 161 return result; 162 } catch (IOException | XmlPullParserException e) { 163 ProvisionLogger.loge("Caught exception while trying to load an intent from the" 164 + " IntentStore", e); 165 return null; 166 } 167 } 168 } 169 parseIntent(XmlPullParser parser)170 private Intent parseIntent(XmlPullParser parser) throws XmlPullParserException, IOException { 171 Intent intent = new Intent(); 172 int outerDepth = parser.getDepth(); 173 int type; 174 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 175 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 176 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 177 continue; 178 } 179 String tag = parser.getName(); 180 if (tag.equals(TAG_ACTION)) { 181 intent.setAction(parser.getAttributeValue(null, ATTR_VALUE)); 182 } else if (tag.equals(TAG_COMPONENT_NAME)) { 183 intent.setComponent(ComponentName.unflattenFromString( 184 parser.getAttributeValue(null, ATTR_VALUE))); 185 } else if (tag.equals(TAG_EXTRAS)) { 186 parseExtras(intent, parser); 187 } 188 } 189 return intent; 190 } 191 parseExtras(Intent intent, XmlPullParser parser)192 private void parseExtras(Intent intent, XmlPullParser parser) throws XmlPullParserException, 193 IOException { 194 int outerDepth = parser.getDepth(); 195 int type; 196 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 197 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 198 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 199 continue; 200 } 201 String tag = parser.getName(); 202 switch (tag) { 203 case TAG_STRING: 204 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), 205 parser.getAttributeValue(null, ATTR_VALUE)); 206 break; 207 case TAG_INTEGER: 208 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), 209 Integer.parseInt(parser.getAttributeValue(null, ATTR_VALUE))); 210 break; 211 case TAG_LONG: 212 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), 213 Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE))); 214 break; 215 case TAG_BOOLEAN: 216 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), 217 Boolean.parseBoolean( 218 parser.getAttributeValue(null, ATTR_VALUE))); 219 break; 220 case TAG_COMPONENT_NAME: 221 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), 222 ComponentName.unflattenFromString( 223 parser.getAttributeValue(null, ATTR_VALUE))); 224 break; 225 case TAG_ACCOUNT: 226 Account a = new Account( 227 parser.getAttributeValue(null, ATTR_ACCOUNT_NAME), 228 parser.getAttributeValue(null, ATTR_ACCOUNT_TYPE)); 229 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), a); 230 break; 231 case TAG_PERSISTABLE_BUNDLE: 232 intent.putExtra(parser.getAttributeValue(null, ATTR_NAME), 233 PersistableBundle.restoreFromXml(parser)); 234 break; 235 } 236 } 237 } 238 clear()239 public void clear() { 240 getFile().delete(); 241 } 242 writeTag(XmlSerializer serializer, String tag, String value)243 private void writeTag(XmlSerializer serializer, String tag, String value) throws IOException { 244 serializer.startTag(null, tag); 245 serializer.attribute(null, ATTR_VALUE, value); 246 serializer.endTag(null, tag); 247 } 248 writeTag(XmlSerializer serializer, String tag, String name, String value)249 private void writeTag(XmlSerializer serializer, String tag, String name, String value) 250 throws IOException { 251 serializer.startTag(null, tag); 252 serializer.attribute(null, ATTR_NAME, name); 253 serializer.attribute(null, ATTR_VALUE, value); 254 serializer.endTag(null, tag); 255 } 256 getFile()257 private File getFile() { 258 return new File(mContext.getFilesDir(), "intent_store_" + mName + ".xml"); 259 } 260 } 261