| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.devicepolicy; |
| |
| import android.annotation.NonNull; |
| import android.app.admin.BundlePolicyValue; |
| import android.app.admin.PackagePolicyKey; |
| import android.app.admin.PolicyKey; |
| import android.os.Bundle; |
| import android.os.Parcelable; |
| import android.util.Log; |
| |
| import com.android.internal.util.XmlUtils; |
| import com.android.modules.utils.TypedXmlPullParser; |
| import com.android.modules.utils.TypedXmlSerializer; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Objects; |
| |
| final class BundlePolicySerializer extends PolicySerializer<Bundle> { |
| |
| private static final String TAG = "BundlePolicySerializer"; |
| |
| private static final String TAG_ENTRY = "entry"; |
| private static final String TAG_VALUE = "value"; |
| private static final String ATTR_KEY = "key"; |
| private static final String ATTR_VALUE_TYPE = "type"; |
| private static final String ATTR_MULTIPLE = "m"; |
| |
| private static final String ATTR_TYPE_STRING_ARRAY = "sa"; |
| private static final String ATTR_TYPE_STRING = "s"; |
| private static final String ATTR_TYPE_BOOLEAN = "b"; |
| private static final String ATTR_TYPE_INTEGER = "i"; |
| private static final String ATTR_TYPE_BUNDLE = "B"; |
| private static final String ATTR_TYPE_BUNDLE_ARRAY = "BA"; |
| |
| @Override |
| void saveToXml(@NonNull PolicyKey policyKey, TypedXmlSerializer serializer, |
| @NonNull Bundle value) throws IOException { |
| Objects.requireNonNull(value); |
| Objects.requireNonNull(policyKey); |
| if (!(policyKey instanceof PackagePolicyKey)) { |
| throw new IllegalArgumentException("policyKey is not of type " |
| + "PackagePolicyKey"); |
| } |
| writeBundle(value, serializer); |
| } |
| |
| @Override |
| BundlePolicyValue readFromXml(TypedXmlPullParser parser) { |
| Bundle bundle = new Bundle(); |
| ArrayList<String> values = new ArrayList<>(); |
| try { |
| final int outerDepth = parser.getDepth(); |
| while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| readBundle(bundle, values, parser); |
| } |
| } catch (XmlPullParserException | IOException e) { |
| Log.e(TAG, "Error parsing Bundle policy.", e); |
| return null; |
| } |
| return new BundlePolicyValue(bundle); |
| } |
| |
| private static void readBundle(Bundle restrictions, ArrayList<String> values, |
| TypedXmlPullParser parser) throws XmlPullParserException, IOException { |
| int type = parser.getEventType(); |
| if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) { |
| String key = parser.getAttributeValue(null, ATTR_KEY); |
| String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE); |
| int count = parser.getAttributeInt(null, ATTR_MULTIPLE, -1); |
| if (count != -1) { |
| values.clear(); |
| while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) { |
| if (type == XmlPullParser.START_TAG |
| && parser.getName().equals(TAG_VALUE)) { |
| values.add(parser.nextText().trim()); |
| count--; |
| } |
| } |
| String [] valueStrings = new String[values.size()]; |
| values.toArray(valueStrings); |
| restrictions.putStringArray(key, valueStrings); |
| } else if (ATTR_TYPE_BUNDLE.equals(valType)) { |
| restrictions.putBundle(key, readBundleEntry(parser, values)); |
| } else if (ATTR_TYPE_BUNDLE_ARRAY.equals(valType)) { |
| final int outerDepth = parser.getDepth(); |
| ArrayList<Bundle> bundleList = new ArrayList<>(); |
| while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| Bundle childBundle = readBundleEntry(parser, values); |
| bundleList.add(childBundle); |
| } |
| restrictions.putParcelableArray(key, |
| bundleList.toArray(new Bundle[bundleList.size()])); |
| } else { |
| String value = parser.nextText().trim(); |
| if (ATTR_TYPE_BOOLEAN.equals(valType)) { |
| restrictions.putBoolean(key, Boolean.parseBoolean(value)); |
| } else if (ATTR_TYPE_INTEGER.equals(valType)) { |
| restrictions.putInt(key, Integer.parseInt(value)); |
| } else { |
| restrictions.putString(key, value); |
| } |
| } |
| } |
| } |
| |
| private static Bundle readBundleEntry(TypedXmlPullParser parser, ArrayList<String> values) |
| throws IOException, XmlPullParserException { |
| Bundle childBundle = new Bundle(); |
| int outerDepth = parser.getDepth(); |
| while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| readBundle(childBundle, values, parser); |
| } |
| return childBundle; |
| } |
| |
| private static void writeBundle(Bundle restrictions, TypedXmlSerializer serializer) |
| throws IOException { |
| for (String key : restrictions.keySet()) { |
| Object value = restrictions.get(key); |
| serializer.startTag(null, TAG_ENTRY); |
| serializer.attribute(null, ATTR_KEY, key); |
| |
| if (value instanceof Boolean) { |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN); |
| serializer.text(value.toString()); |
| } else if (value instanceof Integer) { |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER); |
| serializer.text(value.toString()); |
| } else if (value == null || value instanceof String) { |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING); |
| serializer.text(value != null ? (String) value : ""); |
| } else if (value instanceof Bundle) { |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE); |
| writeBundle((Bundle) value, serializer); |
| } else if (value instanceof Parcelable[]) { |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE_ARRAY); |
| Parcelable[] array = (Parcelable[]) value; |
| for (Parcelable parcelable : array) { |
| if (!(parcelable instanceof Bundle)) { |
| throw new IllegalArgumentException("bundle-array can only hold Bundles"); |
| } |
| serializer.startTag(null, TAG_ENTRY); |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE); |
| writeBundle((Bundle) parcelable, serializer); |
| serializer.endTag(null, TAG_ENTRY); |
| } |
| } else { |
| serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY); |
| String[] values = (String[]) value; |
| serializer.attributeInt(null, ATTR_MULTIPLE, values.length); |
| for (String choice : values) { |
| serializer.startTag(null, TAG_VALUE); |
| serializer.text(choice != null ? choice : ""); |
| serializer.endTag(null, TAG_VALUE); |
| } |
| } |
| serializer.endTag(null, TAG_ENTRY); |
| } |
| } |
| } |