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

Commit 4d003eaf authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Automerger Merge Worker
Browse files

Merge changes I09ac3f34,Id3e36922 into sc-dev am: c27e46f5

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14522711

Change-Id: I5cc78a7294946ef25f5fff4e9f9b2652c1f8aabe
parents 847e4fab c27e46f5
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -30,7 +30,5 @@ import java.util.List;
interface IFontManager {
    FontConfig getFontConfig();

    int updateFontFile(in FontUpdateRequest request, int baseVersion);

    int updateFontFamily(in List<FontUpdateRequest> request, int baseVersion);
}
+9 −3
Original line number Diff line number Diff line
@@ -262,8 +262,14 @@ public final class SystemFonts {
     */
    @VisibleForTesting
    public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
        return buildSystemFallback(fontConfig, new ArrayMap<>());
    }

    /** @hide */
    @VisibleForTesting
    public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig,
            ArrayMap<String, ByteBuffer> outBufferCache) {
        final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
        final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
        final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();

        final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -273,7 +279,7 @@ public final class SystemFonts {
            if (familyName == null) {
                continue;
            }
            appendNamedFamily(xmlFamily, bufferCache, fallbackListMap);
            appendNamedFamily(xmlFamily, outBufferCache, fallbackListMap);
        }

        // Then, add fallback fonts to the each fallback map.
@@ -282,7 +288,7 @@ public final class SystemFonts {
            // The first family (usually the sans-serif family) is always placed immediately
            // after the primary family in the fallback.
            if (i == 0 || xmlFamily.getName() == null) {
                pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache);
                pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
            }
        }

+54 −136
Original line number Diff line number Diff line
@@ -20,25 +20,19 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontFileUtil;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
import android.system.ErrnoException;
import android.text.FontConfig;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Slog;

@@ -52,19 +46,17 @@ import com.android.server.SystemService;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/** A service for managing system fonts. */
// TODO(b/173619554): Add API to update fonts.
public final class FontManagerService extends IFontManager.Stub {
    private static final String TAG = "FontManagerService";

@@ -77,23 +69,6 @@ public final class FontManagerService extends IFontManager.Stub {
        return getSystemFontConfig();
    }

    @Override
    public int updateFontFile(@NonNull FontUpdateRequest request, int baseVersion) {
        Preconditions.checkArgumentNonnegative(baseVersion);
        Objects.requireNonNull(request);
        Objects.requireNonNull(request.getFd());
        Objects.requireNonNull(request.getSignature());
        getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
                "UPDATE_FONTS permission required.");
        try {
            update(baseVersion, Collections.singletonList(request));
            return FontManager.RESULT_SUCCESS;
        } catch (SystemFontException e) {
            Slog.e(TAG, "Failed to update font file", e);
            return e.getErrorCode();
        }
    }

    @Override
    public int updateFontFamily(@NonNull List<FontUpdateRequest> requests, int baseVersion) {
        Preconditions.checkArgumentNonnegative(baseVersion);
@@ -106,6 +81,17 @@ public final class FontManagerService extends IFontManager.Stub {
        } catch (SystemFontException e) {
            Slog.e(TAG, "Failed to update font family", e);
            return e.getErrorCode();
        } finally {
            for (FontUpdateRequest request : requests) {
                ParcelFileDescriptor fd = request.getFd();
                if (fd != null) {
                    try {
                        fd.close();
                    } catch (IOException e) {
                        Slog.w(TAG, "Failed to close fd", e);
                    }
                }
            }
        }
    }

@@ -154,89 +140,6 @@ public final class FontManagerService extends IFontManager.Stub {
        }
    }

    /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
        @Override
        public String getPostScriptName(File file) throws IOException {
            ByteBuffer buffer = mmap(file);
            try {
                return FontFileUtil.getPostScriptName(buffer, 0);
            } finally {
                NioUtils.freeDirectBuffer(buffer);
            }
        }

        @Override
        public String buildFontFileName(File file) throws IOException {
            ByteBuffer buffer = mmap(file);
            try {
                String psName = FontFileUtil.getPostScriptName(buffer, 0);
                int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
                int isCollection = FontFileUtil.isCollectionFont(buffer);

                if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
                    return null;
                }

                String extension;
                if (isCollection == 1) {
                    extension = isType1Font == 1 ? ".otc" : ".ttc";
                } else {
                    extension = isType1Font == 1 ? ".otf" : ".ttf";
                }
                return psName + extension;
            } finally {
                NioUtils.freeDirectBuffer(buffer);
            }

        }

        @Override
        public long getRevision(File file) throws IOException {
            ByteBuffer buffer = mmap(file);
            try {
                return FontFileUtil.getRevision(buffer, 0);
            } finally {
                NioUtils.freeDirectBuffer(buffer);
            }
        }

        @Override
        public void tryToCreateTypeface(File file) throws Throwable {
            Font font = new Font.Builder(file).build();
            FontFamily family = new FontFamily.Builder(font).build();
            Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();

            TextPaint p = new TextPaint();
            p.setTextSize(24f);
            p.setTypeface(typeface);

            // Test string to try with the passed font.
            // TODO: Good to extract from font file.
            String testTextToDraw = "abcXYZ@- "
                    + "\uD83E\uDED6" // Emoji E13.0
                    + "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
                    + "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
                    // ZWJ Sequence
                    + "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
                    + "\uD83D\uDC68\uD83C\uDFFF";

            int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
            StaticLayout layout = StaticLayout.Builder.obtain(
                    testTextToDraw, 0, testTextToDraw.length(), p, width).build();
            Bitmap bmp = Bitmap.createBitmap(
                    layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
            Canvas canvas = new Canvas(bmp);
            layout.draw(canvas);
        }

        private static ByteBuffer mmap(File file) throws IOException {
            try (FileInputStream in = new FileInputStream(file)) {
                FileChannel fileChannel = in.getChannel();
                return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
            }
        }
    }

    private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil {
        @Override
        public boolean hasFsverity(String filePath) {
@@ -289,13 +192,7 @@ public final class FontManagerService extends IFontManager.Stub {
    private void initialize() {
        synchronized (mUpdatableFontDirLock) {
            if (mUpdatableFontDir == null) {
                synchronized (mSerializedFontMapLock) {
                    try {
                        mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
                    } catch (IOException | ErrnoException e) {
                        mSerializedFontMap = null;
                    }
                }
                setSerializedFontMap(serializeSystemServerFontMap());
                return;
            }
            mUpdatableFontDir.loadFontFileMap();
@@ -391,36 +288,57 @@ public final class FontManagerService extends IFontManager.Stub {
    /**
     * Makes new serialized font map data and updates mSerializedFontMap.
     */
    public void updateSerializedFontMap() {
    private void updateSerializedFontMap() {
        SharedMemory serializedFontMap = serializeFontMap(getSystemFontConfig());
        if (serializedFontMap == null) {
            // Fallback to the preloaded config.
            serializedFontMap = serializeSystemServerFontMap();
        }
        setSerializedFontMap(serializedFontMap);
    }

    @Nullable
    private static SharedMemory serializeFontMap(FontConfig fontConfig) {
        final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
        try {
            final FontConfig fontConfig = getSystemFontConfig();
            final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
            final Map<String, FontFamily[]> fallback =
                    SystemFonts.buildSystemFallback(fontConfig, bufferCache);
            final Map<String, Typeface> typefaceMap =
                    SystemFonts.buildSystemTypefaces(fontConfig, fallback);

            SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
            synchronized (mSerializedFontMapLock) {
                mSerializedFontMap = serializeFontMap;
            }
            return;
            return Typeface.serializeFontMap(typefaceMap);
        } catch (IOException | ErrnoException e) {
            Slog.w(TAG, "Failed to serialize updatable font map. "
                    + "Retrying with system image fonts.", e);
            return null;
        } finally {
            // Unmap buffers promptly, as we map a lot of files and may hit mmap limit before
            // GC collects ByteBuffers and unmaps them.
            for (ByteBuffer buffer : bufferCache.values()) {
                if (buffer instanceof DirectByteBuffer) {
                    NioUtils.freeDirectBuffer(buffer);
                }
            }
        }
    }

    @Nullable
    private static SharedMemory serializeSystemServerFontMap() {
        try {
            final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
            final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
            final Map<String, Typeface> typefaceMap =
                    SystemFonts.buildSystemTypefaces(fontConfig, fallback);

            SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
            synchronized (mSerializedFontMapLock) {
                mSerializedFontMap = serializeFontMap;
            }
            return Typeface.serializeFontMap(Typeface.getSystemFontMap());
        } catch (IOException | ErrnoException e) {
            Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
            return null;
        }
    }

    private void setSerializedFontMap(SharedMemory serializedFontMap) {
        SharedMemory oldFontMap = null;
        synchronized (mSerializedFontMapLock) {
            oldFontMap = mSerializedFontMap;
            mSerializedFontMap = serializedFontMap;
        }
        if (oldFontMap != null) {
            oldFontMap.close();
        }
    }
}
+130 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.graphics.fonts;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontFileUtil;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;

/* package */  class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
    @Override
    public String getPostScriptName(File file) throws IOException {
        ByteBuffer buffer = mmap(file);
        try {
            return FontFileUtil.getPostScriptName(buffer, 0);
        } finally {
            unmap(buffer);
        }
    }

    @Override
    public String buildFontFileName(File file) throws IOException {
        ByteBuffer buffer = mmap(file);
        try {
            String psName = FontFileUtil.getPostScriptName(buffer, 0);
            int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
            int isCollection = FontFileUtil.isCollectionFont(buffer);

            if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
                return null;
            }

            String extension;
            if (isCollection == 1) {
                extension = isType1Font == 1 ? ".otc" : ".ttc";
            } else {
                extension = isType1Font == 1 ? ".otf" : ".ttf";
            }
            return psName + extension;
        } finally {
            unmap(buffer);
        }

    }

    @Override
    public long getRevision(File file) throws IOException {
        ByteBuffer buffer = mmap(file);
        try {
            return FontFileUtil.getRevision(buffer, 0);
        } finally {
            unmap(buffer);
        }
    }

    @Override
    public void tryToCreateTypeface(File file) throws Throwable {
        ByteBuffer buffer = mmap(file);
        try {
            Font font = new Font.Builder(buffer).build();
            FontFamily family = new FontFamily.Builder(font).build();
            Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();

            TextPaint p = new TextPaint();
            p.setTextSize(24f);
            p.setTypeface(typeface);

            // Test string to try with the passed font.
            // TODO: Good to extract from font file.
            String testTextToDraw = "abcXYZ@- "
                    + "\uD83E\uDED6" // Emoji E13.0
                    + "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
                    + "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
                    // ZWJ Sequence
                    + "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
                    + "\uD83D\uDC68\uD83C\uDFFF";

            int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
            StaticLayout layout = StaticLayout.Builder.obtain(
                    testTextToDraw, 0, testTextToDraw.length(), p, width).build();
            Bitmap bmp = Bitmap.createBitmap(
                    layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
            Canvas canvas = new Canvas(bmp);
            layout.draw(canvas);
        } finally {
            unmap(buffer);
        }
    }

    private static ByteBuffer mmap(File file) throws IOException {
        try (FileInputStream in = new FileInputStream(file)) {
            FileChannel fileChannel = in.getChannel();
            return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
        }
    }

    private static void unmap(ByteBuffer buffer) {
        if (buffer instanceof DirectByteBuffer) {
            NioUtils.freeDirectBuffer(buffer);
        }
    }
}
+68 −122

File changed.

Preview size limit exceeded, changes collapsed.

Loading