1 /* 2 * Copyright (C) 2022 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 com.android.server.devicepolicy; 18 19 import android.annotation.NonNull; 20 import android.app.admin.BundlePolicyValue; 21 import android.os.Bundle; 22 import android.os.Parcelable; 23 import android.util.Log; 24 25 import com.android.internal.util.XmlUtils; 26 import com.android.modules.utils.TypedXmlPullParser; 27 import com.android.modules.utils.TypedXmlSerializer; 28 29 import org.xmlpull.v1.XmlPullParser; 30 import org.xmlpull.v1.XmlPullParserException; 31 32 import java.io.IOException; 33 import java.util.ArrayList; 34 import java.util.Objects; 35 36 final class BundlePolicySerializer extends PolicySerializer<Bundle> { 37 38 private static final String TAG = "BundlePolicySerializer"; 39 40 private static final String TAG_ENTRY = "entry"; 41 private static final String TAG_VALUE = "value"; 42 private static final String ATTR_KEY = "key"; 43 private static final String ATTR_VALUE_TYPE = "type"; 44 private static final String ATTR_MULTIPLE = "m"; 45 46 private static final String ATTR_TYPE_STRING_ARRAY = "sa"; 47 private static final String ATTR_TYPE_STRING = "s"; 48 private static final String ATTR_TYPE_BOOLEAN = "b"; 49 private static final String ATTR_TYPE_INTEGER = "i"; 50 private static final String ATTR_TYPE_BUNDLE = "B"; 51 private static final String ATTR_TYPE_BUNDLE_ARRAY = "BA"; 52 53 @Override saveToXml(TypedXmlSerializer serializer, @NonNull Bundle value)54 void saveToXml(TypedXmlSerializer serializer, @NonNull Bundle value) throws IOException { 55 Objects.requireNonNull(value); 56 writeBundle(value, serializer); 57 } 58 59 @Override readFromXml(TypedXmlPullParser parser)60 BundlePolicyValue readFromXml(TypedXmlPullParser parser) { 61 Bundle bundle = new Bundle(); 62 ArrayList<String> values = new ArrayList<>(); 63 try { 64 final int outerDepth = parser.getDepth(); 65 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 66 readBundle(bundle, values, parser); 67 } 68 } catch (XmlPullParserException | IOException e) { 69 Log.e(TAG, "Error parsing Bundle policy.", e); 70 return null; 71 } 72 return new BundlePolicyValue(bundle); 73 } 74 readBundle(Bundle restrictions, ArrayList<String> values, TypedXmlPullParser parser)75 private static void readBundle(Bundle restrictions, ArrayList<String> values, 76 TypedXmlPullParser parser) throws XmlPullParserException, IOException { 77 int type = parser.getEventType(); 78 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) { 79 String key = parser.getAttributeValue(null, ATTR_KEY); 80 String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE); 81 int count = parser.getAttributeInt(null, ATTR_MULTIPLE, -1); 82 if (count != -1) { 83 values.clear(); 84 while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) { 85 if (type == XmlPullParser.START_TAG 86 && parser.getName().equals(TAG_VALUE)) { 87 values.add(parser.nextText()); 88 count--; 89 } 90 } 91 String [] valueStrings = new String[values.size()]; 92 values.toArray(valueStrings); 93 restrictions.putStringArray(key, valueStrings); 94 } else if (ATTR_TYPE_BUNDLE.equals(valType)) { 95 restrictions.putBundle(key, readBundleEntry(parser, values)); 96 } else if (ATTR_TYPE_BUNDLE_ARRAY.equals(valType)) { 97 final int outerDepth = parser.getDepth(); 98 ArrayList<Bundle> bundleList = new ArrayList<>(); 99 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 100 Bundle childBundle = readBundleEntry(parser, values); 101 bundleList.add(childBundle); 102 } 103 restrictions.putParcelableArray(key, 104 bundleList.toArray(new Bundle[bundleList.size()])); 105 } else { 106 String value = parser.nextText(); 107 if (ATTR_TYPE_BOOLEAN.equals(valType)) { 108 restrictions.putBoolean(key, Boolean.parseBoolean(value)); 109 } else if (ATTR_TYPE_INTEGER.equals(valType)) { 110 restrictions.putInt(key, Integer.parseInt(value)); 111 } else { 112 restrictions.putString(key, value); 113 } 114 } 115 } 116 } 117 readBundleEntry(TypedXmlPullParser parser, ArrayList<String> values)118 private static Bundle readBundleEntry(TypedXmlPullParser parser, ArrayList<String> values) 119 throws IOException, XmlPullParserException { 120 Bundle childBundle = new Bundle(); 121 int outerDepth = parser.getDepth(); 122 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 123 readBundle(childBundle, values, parser); 124 } 125 return childBundle; 126 } 127 writeBundle(Bundle restrictions, TypedXmlSerializer serializer)128 private static void writeBundle(Bundle restrictions, TypedXmlSerializer serializer) 129 throws IOException { 130 for (String key : restrictions.keySet()) { 131 Object value = restrictions.get(key); 132 serializer.startTag(null, TAG_ENTRY); 133 serializer.attribute(null, ATTR_KEY, key); 134 135 if (value instanceof Boolean) { 136 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN); 137 serializer.text(value.toString()); 138 } else if (value instanceof Integer) { 139 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER); 140 serializer.text(value.toString()); 141 } else if (value == null || value instanceof String) { 142 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING); 143 serializer.text(value != null ? (String) value : ""); 144 } else if (value instanceof Bundle) { 145 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE); 146 writeBundle((Bundle) value, serializer); 147 } else if (value instanceof Parcelable[]) { 148 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE_ARRAY); 149 Parcelable[] array = (Parcelable[]) value; 150 for (Parcelable parcelable : array) { 151 if (!(parcelable instanceof Bundle)) { 152 throw new IllegalArgumentException("bundle-array can only hold Bundles"); 153 } 154 serializer.startTag(null, TAG_ENTRY); 155 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE); 156 writeBundle((Bundle) parcelable, serializer); 157 serializer.endTag(null, TAG_ENTRY); 158 } 159 } else { 160 serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY); 161 String[] values = (String[]) value; 162 serializer.attributeInt(null, ATTR_MULTIPLE, values.length); 163 for (String choice : values) { 164 serializer.startTag(null, TAG_VALUE); 165 serializer.text(choice != null ? choice : ""); 166 serializer.endTag(null, TAG_VALUE); 167 } 168 } 169 serializer.endTag(null, TAG_ENTRY); 170 } 171 } 172 } 173