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