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

Commit 4db7bd28 authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

Consolidate AssetManager calls in Font/FontFamily

The Font/FontFamily classes use private API calls in native
code to get the contents of an Asset.  These calls need to
be made on public APIs in order to isolate the graphics module
from the rest of the system.  Public Java APIs already exist so
this CL refactors the code to use those methods.

Test: CtsTextTestCases
Bug: 147136234
Change-Id: I35a6558b597684e59279d7a841669c5f909d5152
parent 929de647
Loading
Loading
Loading
Loading
+0 −61
Original line number Diff line number Diff line
@@ -27,8 +27,6 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"

@@ -212,63 +210,6 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong
    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}

static void releaseAsset(const void* ptr, void* context) {
    delete static_cast<Asset*>(context);
}

static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr,
        jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex,
        jint weight, jint isItalic) {
#ifdef __ANDROID__ // Layoutlib does not support native AssetManager
    NPE_CHECK_RETURN_ZERO(env, jassetMgr);
    NPE_CHECK_RETURN_ZERO(env, jpath);

    NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
    Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
    if (NULL == mgr) {
        builder->axes.clear();
        return false;
    }

    ScopedUtfChars str(env, jpath);
    if (str.c_str() == nullptr) {
        builder->axes.clear();
        return false;
    }

    std::unique_ptr<Asset> asset;
    {
      ScopedLock<AssetManager2> locked_mgr(*mgr);
      if (isAsset) {
          asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
      } else if (cookie > 0) {
          // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
          asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
                  Asset::ACCESS_BUFFER);
      } else {
          asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
      }
    }

    if (nullptr == asset) {
        builder->axes.clear();
        return false;
    }

    const void* buf = asset->getBuffer(false);
    if (NULL == buf) {
        builder->axes.clear();
        return false;
    }

    sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
            asset.release()));
    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
#else
    return false;
#endif
}

static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) {
    NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
    builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value});
@@ -284,8 +225,6 @@ static const JNINativeMethod gFontFamilyMethods[] = {
    { "nAddFont",               "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
    { "nAddFontWeightStyle",    "(JLjava/nio/ByteBuffer;III)Z",
            (void*)FontFamily_addFontWeightStyle },
    { "nAddFontFromAssetManager",    "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z",
            (void*)FontFamily_addFontFromAssetManager },
    { "nAddAxisValue",         "(JIF)V", (void*)FontFamily_addAxisValue },
};

+0 −63
Original line number Diff line number Diff line
@@ -26,8 +26,6 @@
#include "GraphicsJNI.h"
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"

@@ -48,14 +46,6 @@ static inline NativeFontBuilder* toBuilder(jlong ptr) {
    return reinterpret_cast<NativeFontBuilder*>(ptr);
}

static inline Asset* toAsset(jlong ptr) {
    return reinterpret_cast<Asset*>(ptr);
}

static void releaseAsset(jlong asset) {
    delete toAsset(asset);
}

static void releaseFont(jlong font) {
    delete reinterpret_cast<FontWrapper*>(font);
}
@@ -78,54 +68,6 @@ static void release_global_ref(const void* /*data*/, void* context) {
    env->DeleteGlobalRef(obj);
}

// Regular JNI
static jlong Font_Builder_getNativeAsset(
    JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) {
#ifdef __ANDROID__ // Layoutlib does not support native AssetManager
    NPE_CHECK_RETURN_ZERO(env, assetMgr);
    NPE_CHECK_RETURN_ZERO(env, path);

    Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr);
    if (mgr == nullptr) {
        return 0;
    }

    ScopedUtfChars str(env, path);
    if (str.c_str() == nullptr) {
        return 0;
    }

    std::unique_ptr<Asset> asset;
    {
      ScopedLock<AssetManager2> locked_mgr(*mgr);
      if (isAsset) {
          asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
      } else if (cookie > 0) {
          // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
          asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
                  Asset::ACCESS_BUFFER);
      } else {
          asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
      }
    }

    return reinterpret_cast<jlong>(asset.release());
#else
    return 0;
#endif
}

// Regular JNI
static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) {
    Asset* asset = toAsset(nativeAsset);
    return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength());
}

// CriticalNative
static jlong Font_Builder_getReleaseNativeAssetFunc(CRITICAL_JNI_PARAMS) {
    return reinterpret_cast<jlong>(&releaseAsset);
}

// Regular JNI
static jlong Font_Builder_initBuilder(JNIEnv*, jobject) {
    return reinterpret_cast<jlong>(new NativeFontBuilder());
@@ -196,11 +138,6 @@ static const JNINativeMethod gFontBuilderMethods[] = {
    { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
    { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
    { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },

    { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J",
      (void*) Font_Builder_getNativeAsset },
    { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer },
    { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc },
};

int register_android_graphics_fonts_Font(JNIEnv* env) {
+7 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.text.TextUtils;
@@ -195,18 +196,13 @@ public class FontFamily {
        if (mBuilderPtr == 0) {
            throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
        }
        if (axes != null) {
            for (FontVariationAxis axis : axes) {
                nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
            }
        }
        return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
                isItalic);
    }

    // TODO: Remove once internal user stop using private API.
    private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
        return nAddFont(builderPtr, font, ttcIndex, -1, -1);
        try {
            ByteBuffer buffer =  Font.Builder.createBuffer(mgr, path, isAsset, cookie);
            return addFontFromBuffer(buffer, ttcIndex, axes, weight, isItalic);
        } catch (IOException e) {
            return false;
        }
    }

    private static native long nInitBuilder(String langs, int variant);
@@ -225,8 +221,6 @@ public class FontFamily {
            int weight, int isItalic);
    private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
            int ttcIndex, int weight, int isItalic);
    private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
            String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);

    // The added axis values are only valid for the next nAddFont* method call.
    @CriticalNative
+68 −36
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.graphics.fonts;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.LocaleList;
@@ -35,7 +36,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Objects;
@@ -54,10 +57,6 @@ public final class Font {
     * A builder class for creating new Font.
     */
    public static final class Builder {
        private static final NativeAllocationRegistry sAssetByteBufferRegistry =
                NativeAllocationRegistry.createMalloced(ByteBuffer.class.getClassLoader(),
                    nGetReleaseNativeAssetFunc());

        private static final NativeAllocationRegistry sFontRegistry =
                NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
                    nGetReleaseNativeFont());
@@ -151,7 +150,11 @@ public final class Font {
         * @param path the file name of the font data in the asset directory
         */
        public Builder(@NonNull AssetManager am, @NonNull String path) {
            this(am, path, true /* is asset */, 0 /* cookie */);
            try {
                mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */);
            } catch (IOException e) {
                mException = e;
            }
        }

        /**
@@ -165,18 +168,11 @@ public final class Font {
         */
        public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset,
                int cookie) {
            final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie);
            if (nativeAsset == 0) {
                mException = new FileNotFoundException("Unable to open " + path);
                return;
            }
            final ByteBuffer b = nGetAssetBuffer(nativeAsset);
            sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
            if (b == null) {
                mException = new FileNotFoundException(path + " not found");
                return;
            try {
                mBuffer = createBuffer(am, path, isAsset, cookie);
            } catch (IOException e) {
                mException = e;
            }
            mBuffer = b;
        }

        /**
@@ -199,19 +195,64 @@ public final class Font {
                mException = new FileNotFoundException(resId + " must be font file.");
                return;
            }
            final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */,
                    value.assetCookie);
            if (nativeAsset == 0) {
                mException = new FileNotFoundException("Unable to open " + str);
                return;

            try {
                mBuffer = createBuffer(res.getAssets(), str, false, value.assetCookie);
            } catch (IOException e) {
                mException = e;
            }
            final ByteBuffer b = nGetAssetBuffer(nativeAsset);
            sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
            if (b == null) {
                mException = new FileNotFoundException(str + " not found");
                return;
        }
            mBuffer = b;

        /**
         * Creates a buffer containing font data using the assetManager and other
         * provided inputs.
         *
         * @param am the application's asset manager
         * @param path the file name of the font data in the asset directory
         * @param isAsset true if the undelying data is in asset
         * @param cookie set asset cookie
         * @return buffer containing the contents of the file
         *
         * @hide
         */
        public static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
                                              boolean isAsset, int cookie) throws IOException {
            Preconditions.checkNotNull(am, "assetManager can not be null");
            Preconditions.checkNotNull(path, "path can not be null");

            if (!isAsset) {
                // Attempt to open as FD, which should work unless the asset is compressed
                AssetFileDescriptor assetFD;
                try {
                    if (cookie > 0) {
                        assetFD = am.openNonAssetFd(cookie, path);
                    } else {
                        assetFD = am.openNonAssetFd(path);
                    }

                    try (FileInputStream fis = assetFD.createInputStream()) {
                        final FileChannel fc = fis.getChannel();
                        return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
                    }
                } catch (IOException e) {
                    // failed to open as FD so now we will attempt to open as an input stream
                }
            }

            try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER)
                    : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {

                int capacity = assetStream.available();
                ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
                buffer.order(ByteOrder.nativeOrder());
                assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());

                if (assetStream.read() != -1) {
                    throw new IOException("Unable to access full contents of " + path);
                }

                return buffer;
            }
        }

        /**
@@ -395,15 +436,6 @@ public final class Font {
            return font;
        }

        /**
         * Native methods for accessing underlying buffer in Asset
         */
        private static native long nGetNativeAsset(
                @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie);
        private static native ByteBuffer nGetAssetBuffer(long nativeAsset);
        @CriticalNative
        private static native long nGetReleaseNativeAssetFunc();

        /**
         * Native methods for creating Font
         */