Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a6f5495 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Android (Google) Code Review
Browse files

Merge "Support font family update internally." into sc-dev

parents 58e34307 9aa5c97a
Loading
Loading
Loading
Loading
+50 −7
Original line number Diff line number Diff line
@@ -16,18 +16,33 @@

package android.graphics.fonts;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.text.FontConfig;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Represents a font update request. Currently only font install request is supported.
 * @hide
 */
// TODO: Support font config update.
public final class FontUpdateRequest implements Parcelable {

    public static final int TYPE_UPDATE_FONT_FILE = 0;
    public static final int TYPE_UPDATE_FONT_FAMILY = 1;

    @IntDef(prefix = "TYPE_", value = {
            TYPE_UPDATE_FONT_FILE,
            TYPE_UPDATE_FONT_FAMILY,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Type {}

    public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() {
        @Override
        public FontUpdateRequest createFromParcel(Parcel in) {
@@ -40,39 +55,67 @@ public final class FontUpdateRequest implements Parcelable {
        }
    };

    @NonNull
    private final @Type int mType;
    // NonNull if mType == TYPE_UPDATE_FONT_FILE.
    @Nullable
    private final ParcelFileDescriptor mFd;
    @NonNull
    // NonNull if mType == TYPE_UPDATE_FONT_FILE.
    @Nullable
    private final byte[] mSignature;
    // NonNull if mType == TYPE_UPDATE_FONT_FAMILY.
    @Nullable
    private final FontConfig.FontFamily mFontFamily;

    public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) {
        mType = TYPE_UPDATE_FONT_FILE;
        mFd = fd;
        mSignature = signature;
        mFontFamily = null;
    }

    public FontUpdateRequest(@NonNull FontConfig.FontFamily fontFamily) {
        mType = TYPE_UPDATE_FONT_FAMILY;
        mFd = null;
        mSignature = null;
        mFontFamily = fontFamily;
    }

    private FontUpdateRequest(Parcel in) {
    protected FontUpdateRequest(Parcel in) {
        mType = in.readInt();
        mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
        mSignature = in.readBlob();
        mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader());
    }

    @NonNull
    public @Type int getType() {
        return mType;
    }

    @Nullable
    public ParcelFileDescriptor getFd() {
        return mFd;
    }

    @NonNull
    @Nullable
    public byte[] getSignature() {
        return mSignature;
    }

    @Nullable
    public FontConfig.FontFamily getFontFamily() {
        return mFontFamily;
    }

    @Override
    public int describeContents() {
        return Parcelable.CONTENTS_FILE_DESCRIPTOR;
        return mFd != null ? mFd.describeContents() : 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mType);
        dest.writeParcelable(mFd, flags);
        dest.writeBlob(mSignature);
        dest.writeParcelable(mFontFamily, flags);
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server.graphics.fonts;

import android.annotation.NonNull;
import android.graphics.FontListParser;
import android.text.FontConfig;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
@@ -30,6 +32,8 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/* package */ class PersistentSystemFontConfig {
@@ -38,11 +42,13 @@ import java.util.Set;
    private static final String TAG_ROOT = "fontConfig";
    private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate";
    private static final String TAG_UPDATED_FONT_DIR = "updatedFontDir";
    private static final String TAG_FAMILY = "family";
    private static final String ATTR_VALUE = "value";

    /* package */ static class Config {
        public long lastModifiedDate;
        public final Set<String> updatedFontDirs = new ArraySet<>();
        public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>();
    }

    /**
@@ -72,6 +78,11 @@ import java.util.Set;
                    case TAG_UPDATED_FONT_DIR:
                        out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE));
                        break;
                    case TAG_FAMILY:
                        // updatableFontMap is not ready here. We get the base file names by passing
                        // empty fontDir, and resolve font paths later.
                        out.fontFamilies.add(FontListParser.readFamily(
                                parser, "" /* fontDir */, null /* updatableFontMap */));
                    default:
                        Slog.w(TAG, "Skipping unknown tag: " + tag);
                }
@@ -97,6 +108,13 @@ import java.util.Set;
            out.attribute(null, ATTR_VALUE, dir);
            out.endTag(null, TAG_UPDATED_FONT_DIR);
        }
        List<FontConfig.FontFamily> fontFamilies = config.fontFamilies;
        for (int i = 0; i < fontFamilies.size(); i++) {
            FontConfig.FontFamily fontFamily = fontFamilies.get(i);
            out.startTag(null, TAG_FAMILY);
            FontListParser.writeFamily(out, fontFamily);
            out.endTag(null, TAG_FAMILY);
        }
        out.endTag(null, TAG_ROOT);

        out.endDocument();
+94 −5
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import android.util.ArrayMap;
import android.util.Base64;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
@@ -40,6 +42,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@@ -118,6 +121,12 @@ final class UpdatableFontDir {
     */
    private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();

    /**
     * A mutable map containing mapping from font family name to {@link FontConfig.FontFamily}.
     * The FontFamily entries only reference font files in {@link #mFontFileInfoMap}.
     */
    private final ArrayMap<String, FontConfig.FontFamily> mFontFamilyMap = new ArrayMap<>();

    UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
            FsverityUtil fsverityUtil) {
        this(filesDir, preinstalledFontDirs, parser, fsverityUtil, new File(CONFIG_XML_FILE));
@@ -136,6 +145,7 @@ final class UpdatableFontDir {

    /* package */ void loadFontFileMap() {
        mFontFileInfoMap.clear();
        mFontFamilyMap.clear();
        mLastModifiedDate = 0;
        boolean success = false;
        try {
@@ -168,6 +178,13 @@ final class UpdatableFontDir {
                FontFileInfo fontFileInfo = validateFontFile(files[0]);
                addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
            }
            // Resolve font file paths.
            List<FontConfig.FontFamily> fontFamilies = config.fontFamilies;
            for (int i = 0; i < fontFamilies.size(); i++) {
                FontConfig.FontFamily fontFamily = fontFamilies.get(i);
                // Ignore failures as updated fonts may be obsoleted by system OTA update.
                addFontFamily(fontFamily);
            }
            success = true;
        } catch (Throwable t) {
            // If something happened during loading system fonts, clear all contents in finally
@@ -177,6 +194,7 @@ final class UpdatableFontDir {
            // Delete all files just in case if we find a problematic file.
            if (!success) {
                mFontFileInfoMap.clear();
                mFontFamilyMap.clear();
                mLastModifiedDate = 0;
                FileUtils.deleteContents(mFilesDir);
            }
@@ -186,10 +204,11 @@ final class UpdatableFontDir {
    /* package */ void clearUpdates() throws SystemFontException {
        mFontFileInfoMap.clear();
        FileUtils.deleteContents(mFilesDir);
        mFontFamilyMap.clear();

        mLastModifiedDate = Instant.now().getEpochSecond();
        try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
            PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
            PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig());
        } catch (Exception e) {
            throw new SystemFontException(
                    FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
@@ -206,17 +225,29 @@ final class UpdatableFontDir {
    public void update(List<FontUpdateRequest> requests) throws SystemFontException {
        // Backup the mapping for rollback.
        ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
        ArrayMap<String, FontConfig.FontFamily> backupFamilies = new ArrayMap<>(mFontFamilyMap);
        long backupLastModifiedDate = mLastModifiedDate;
        boolean success = false;
        try {
            for (FontUpdateRequest request : requests) {
                installFontFile(request.getFd().getFileDescriptor(), request.getSignature());
                switch (request.getType()) {
                    case FontUpdateRequest.TYPE_UPDATE_FONT_FILE:
                        installFontFile(
                                request.getFd().getFileDescriptor(), request.getSignature());
                        break;
                    case FontUpdateRequest.TYPE_UPDATE_FONT_FAMILY:
                        // TODO: define error code.
                        if (!addFontFamily(request.getFontFamily())) {
                            throw new IllegalArgumentException("Invalid font family");
                        }
                        break;
                }
            }

            // Write config file.
            mLastModifiedDate = Instant.now().getEpochSecond();
            try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
                PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
                PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig());
            } catch (Exception e) {
                throw new SystemFontException(
                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
@@ -234,6 +265,8 @@ final class UpdatableFontDir {
            if (!success) {
                mFontFileInfoMap.clear();
                mFontFileInfoMap.putAll(backupMap);
                mFontFamilyMap.clear();
                mFontFamilyMap.putAll(backupFamilies);
                mLastModifiedDate = backupLastModifiedDate;
            }
        }
@@ -454,12 +487,52 @@ final class UpdatableFontDir {
        }
    }

    private PersistentSystemFontConfig.Config getPersistentConfig() {
    /**
     * Adds a font family to {@link #mFontFamilyMap} and returns true on success.
     *
     * <p>This method only accepts adding or updating a font family with a name.
     * This is to prevent bad font family update from removing glyphs from font fallback chains.
     * Unnamed font families are used as other named font family's fallback fonts to guarantee a
     * complete glyph coverage.
     */
    private boolean addFontFamily(FontConfig.FontFamily fontFamily) {
        if (fontFamily.getName() == null) {
            Slog.e(TAG, "Name is null.");
            return false;
        }
        FontConfig.FontFamily resolvedFontFamily = resolveFontFiles(fontFamily);
        if (resolvedFontFamily == null) {
            Slog.e(TAG, "Required fonts are not available");
            return false;
        }
        mFontFamilyMap.put(resolvedFontFamily.getName(), resolvedFontFamily);
        return true;
    }

    @Nullable
    private FontConfig.FontFamily resolveFontFiles(FontConfig.FontFamily fontFamily) {
        List<FontConfig.Font> resolvedFonts = new ArrayList<>(fontFamily.getFontList().size());
        List<FontConfig.Font> fontList = fontFamily.getFontList();
        for (int i = 0; i < fontList.size(); i++) {
            FontConfig.Font font = fontList.get(i);
            FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName());
            if (info == null) {
                return null;
            }
            resolvedFonts.add(new FontConfig.Font(info.mFile, null, font.getStyle(),
                    font.getTtcIndex(), font.getFontVariationSettings(), font.getFontFamilyName()));
        }
        return new FontConfig.FontFamily(resolvedFonts, fontFamily.getName(),
                fontFamily.getLocaleList(), fontFamily.getVariant());
    }

    private PersistentSystemFontConfig.Config createPersistentConfig() {
        PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
        config.lastModifiedDate = mLastModifiedDate;
        for (FontFileInfo info : mFontFileInfoMap.values()) {
            config.updatedFontDirs.add(info.getRandomizedFontDir().getName());
        }
        config.fontFamilies.addAll(mFontFamilyMap.values());
        return config;
    }

@@ -471,8 +544,24 @@ final class UpdatableFontDir {
        return map;
    }

    @VisibleForTesting
    Map<String, FontConfig.FontFamily> getFontFamilyMap() {
        return mFontFamilyMap;
    }

    /* package */ FontConfig getSystemFontConfig() {
        return SystemFonts.getSystemFontConfig(getFontFileMap(), mLastModifiedDate, mConfigVersion);
        FontConfig config = SystemFonts.getSystemFontConfig(getFontFileMap(), 0, 0);
        List<FontConfig.FontFamily> mergedFamilies =
                new ArrayList<>(config.getFontFamilies().size() + mFontFamilyMap.size());
        // We should keep the first font family (config.getFontFamilies().get(0)) because it's used
        // as a fallback font. See SystemFonts.java.
        mergedFamilies.addAll(config.getFontFamilies());
        // When building Typeface, a latter font family definition will override the previous font
        // family definition with the same name. An exception is config.getFontFamilies.get(0),
        // which will be used as a fallback font without being overridden.
        mergedFamilies.addAll(mFontFamilyMap.values());
        return new FontConfig(
                mergedFamilies, config.getAliases(), mLastModifiedDate, mConfigVersion);
    }

    /* package */ int getConfigVersion() {
+19 −1
Original line number Diff line number Diff line
@@ -18,13 +18,17 @@ package com.android.server.graphics.fonts;

import static com.google.common.truth.Truth.assertThat;

import android.graphics.FontListParser;
import android.platform.test.annotations.Presubmit;
import android.text.FontConfig;
import android.util.Xml;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.ByteArrayInputStream;
@@ -38,13 +42,19 @@ import java.nio.charset.StandardCharsets;
public final class PersistentSystemFontConfigTest {

    @Test
    public void testWriteRead() throws IOException, XmlPullParserException {
    public void testWriteRead() throws Exception {
        long expectedModifiedDate = 1234567890;
        PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
        config.lastModifiedDate = expectedModifiedDate;
        config.updatedFontDirs.add("~~abc");
        config.updatedFontDirs.add("~~def");

        FontConfig.FontFamily fontFamily = parseFontFamily(
                "<family name='test'>"
                + "  <font>test.ttf</font>"
                + "</family>");
        config.fontFamilies.add(fontFamily);

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            PersistentSystemFontConfig.writeToXml(baos, config);

@@ -57,6 +67,7 @@ public final class PersistentSystemFontConfigTest {

                assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
                assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def");
                assertThat(another.fontFamilies).containsExactly(fontFamily);
            }
        }
    }
@@ -75,4 +86,11 @@ public final class PersistentSystemFontConfigTest {
        }
    }

    private static FontConfig.FontFamily parseFontFamily(String xml) throws Exception {
        XmlPullParser parser = Xml.newPullParser();
        ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
        parser.setInput(is, "UTF-8");
        parser.nextTag();
        return FontListParser.readFamily(parser, "", null);
    }
}
+204 −9

File changed.

Preview size limit exceeded, changes collapsed.