1 /* 2 * Copyright (C) 2021 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.graphics.fonts; 18 19 import android.annotation.NonNull; 20 import android.graphics.fonts.FontUpdateRequest; 21 import android.text.TextUtils; 22 import android.util.ArraySet; 23 import android.util.Slog; 24 import android.util.TypedXmlPullParser; 25 import android.util.TypedXmlSerializer; 26 import android.util.Xml; 27 28 import org.xmlpull.v1.XmlPullParser; 29 import org.xmlpull.v1.XmlPullParserException; 30 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Set; 37 38 /* package */ class PersistentSystemFontConfig { 39 private static final String TAG = "PersistentSystemFontConfig"; 40 41 private static final String TAG_ROOT = "fontConfig"; 42 private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate"; 43 private static final String TAG_UPDATED_FONT_DIR = "updatedFontDir"; 44 private static final String TAG_FAMILY = "family"; 45 private static final String ATTR_VALUE = "value"; 46 47 /* package */ static class Config { 48 public long lastModifiedMillis; 49 public final Set<String> updatedFontDirs = new ArraySet<>(); 50 public final List<FontUpdateRequest.Family> fontFamilies = new ArrayList<>(); 51 } 52 53 /** 54 * Read config XML and write to out argument. 55 */ loadFromXml(@onNull InputStream is, @NonNull Config out)56 public static void loadFromXml(@NonNull InputStream is, @NonNull Config out) 57 throws XmlPullParserException, IOException { 58 TypedXmlPullParser parser = Xml.resolvePullParser(is); 59 60 int type; 61 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 62 if (type != XmlPullParser.START_TAG) { 63 continue; 64 } 65 final int depth = parser.getDepth(); 66 final String tag = parser.getName(); 67 if (depth == 1) { 68 if (!TAG_ROOT.equals(tag)) { 69 Slog.e(TAG, "Invalid root tag: " + tag); 70 return; 71 } 72 } else if (depth == 2) { 73 switch (tag) { 74 case TAG_LAST_MODIFIED_DATE: 75 out.lastModifiedMillis = parseLongAttribute(parser, ATTR_VALUE, 0); 76 break; 77 case TAG_UPDATED_FONT_DIR: 78 out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE)); 79 break; 80 case TAG_FAMILY: 81 // updatableFontMap is not ready here. We get the base file names by passing 82 // empty fontDir, and resolve font paths later. 83 out.fontFamilies.add(FontUpdateRequest.Family.readFromXml(parser)); 84 break; 85 default: 86 Slog.w(TAG, "Skipping unknown tag: " + tag); 87 } 88 } 89 } 90 91 } 92 93 /** 94 * Write config to OutputStream as XML file. 95 */ writeToXml(@onNull OutputStream os, @NonNull Config config)96 public static void writeToXml(@NonNull OutputStream os, @NonNull Config config) 97 throws IOException { 98 TypedXmlSerializer out = Xml.resolveSerializer(os); 99 out.startDocument(null /* encoding */, true /* standalone */); 100 101 out.startTag(null, TAG_ROOT); 102 out.startTag(null, TAG_LAST_MODIFIED_DATE); 103 out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedMillis)); 104 out.endTag(null, TAG_LAST_MODIFIED_DATE); 105 for (String dir : config.updatedFontDirs) { 106 out.startTag(null, TAG_UPDATED_FONT_DIR); 107 out.attribute(null, ATTR_VALUE, dir); 108 out.endTag(null, TAG_UPDATED_FONT_DIR); 109 } 110 List<FontUpdateRequest.Family> fontFamilies = config.fontFamilies; 111 for (int i = 0; i < fontFamilies.size(); i++) { 112 FontUpdateRequest.Family fontFamily = fontFamilies.get(i); 113 out.startTag(null, TAG_FAMILY); 114 FontUpdateRequest.Family.writeFamilyToXml(out, fontFamily); 115 out.endTag(null, TAG_FAMILY); 116 } 117 out.endTag(null, TAG_ROOT); 118 119 out.endDocument(); 120 } 121 parseLongAttribute(TypedXmlPullParser parser, String attr, long defValue)122 private static long parseLongAttribute(TypedXmlPullParser parser, String attr, long defValue) { 123 final String value = parser.getAttributeValue(null /* namespace */, attr); 124 if (TextUtils.isEmpty(value)) { 125 return defValue; 126 } 127 try { 128 return Long.parseLong(value); 129 } catch (NumberFormatException e) { 130 return defValue; 131 } 132 } 133 134 @NonNull getAttribute(TypedXmlPullParser parser, String attr)135 private static String getAttribute(TypedXmlPullParser parser, String attr) { 136 final String value = parser.getAttributeValue(null /* namespace */, attr); 137 return value == null ? "" : value; 138 } 139 } 140