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

Commit 3c4be77d authored by Clara Bayarri's avatar Clara Bayarri
Browse files

Support non-system Font Providers

To do this, the developer must specify the set of certificate
hashes that represent the authority's app. This allows us to
verify that the authority we find is indeed the one intended
by the developer.

Bug: 35025705
Test: runtest --path frameworks/base/core/tests/coretests/src/android/provider/FontsContractTest.java
runtest --path frameworks/base/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
CTS attached to topic
Change-Id: I605f9a93bbca8705936ead08efb4a5b4fdcc4882
parent c068aa22
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -607,6 +607,7 @@ package android {
    field public static final int fontFamily = 16843692; // 0x10103ac
    field public static final int fontFeatureSettings = 16843959; // 0x10104b7
    field public static final int fontProviderAuthority = 16844114; // 0x1010552
    field public static final int fontProviderPackage = 16844122; // 0x101055a
    field public static final int fontProviderQuery = 16844115; // 0x1010553
    field public static final int fontStyle = 16844081; // 0x1010531
    field public static final int fontWeight = 16844083; // 0x1010533
@@ -14089,9 +14090,12 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
  public final class FontRequest implements android.os.Parcelable {
    ctor public FontRequest(java.lang.String, java.lang.String);
    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String);
    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
    method public int describeContents();
    method public java.util.List<java.util.List<byte[]>> getCertificates();
    method public java.lang.String getProviderAuthority();
    method public java.lang.String getProviderPackage();
    method public java.lang.String getQuery();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
+5 −1
Original line number Diff line number Diff line
@@ -719,6 +719,7 @@ package android {
    field public static final int fontFamily = 16843692; // 0x10103ac
    field public static final int fontFeatureSettings = 16843959; // 0x10104b7
    field public static final int fontProviderAuthority = 16844114; // 0x1010552
    field public static final int fontProviderPackage = 16844122; // 0x101055a
    field public static final int fontProviderQuery = 16844115; // 0x1010553
    field public static final int fontStyle = 16844081; // 0x1010531
    field public static final int fontWeight = 16844083; // 0x1010533
@@ -14806,9 +14807,12 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
  public final class FontRequest implements android.os.Parcelable {
    ctor public FontRequest(java.lang.String, java.lang.String);
    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String);
    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
    method public int describeContents();
    method public java.util.List<java.util.List<byte[]>> getCertificates();
    method public java.lang.String getProviderAuthority();
    method public java.lang.String getProviderPackage();
    method public java.lang.String getQuery();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
+5 −1
Original line number Diff line number Diff line
@@ -607,6 +607,7 @@ package android {
    field public static final int fontFamily = 16843692; // 0x10103ac
    field public static final int fontFeatureSettings = 16843959; // 0x10104b7
    field public static final int fontProviderAuthority = 16844114; // 0x1010552
    field public static final int fontProviderPackage = 16844122; // 0x101055a
    field public static final int fontProviderQuery = 16844115; // 0x1010553
    field public static final int fontStyle = 16844081; // 0x1010531
    field public static final int fontWeight = 16844083; // 0x1010533
@@ -14128,9 +14129,12 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
  public final class FontRequest implements android.os.Parcelable {
    ctor public FontRequest(java.lang.String, java.lang.String);
    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String);
    ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
    method public int describeContents();
    method public java.util.List<java.util.List<byte[]>> getCertificates();
    method public java.lang.String getProviderAuthority();
    method public java.lang.String getProviderPackage();
    method public java.lang.String getQuery();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
+3 −2
Original line number Diff line number Diff line
@@ -67,13 +67,14 @@ public class FontResourcesParser {
        AttributeSet attrs = Xml.asAttributeSet(parser);
        TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
        String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
        String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
        String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
        array.recycle();
        if (authority != null && query != null) {
        if (authority != null && providerPackage != null && query != null) {
            while (parser.next() != XmlPullParser.END_TAG) {
                skip(parser);
            }
            return new FontConfig.Family(authority, query);
            return new FontConfig.Family(authority, providerPackage, query);
        }
        List<FontConfig.Font> fonts = new ArrayList<>();
        while (parser.next() != XmlPullParser.END_TAG) {
+85 −19
Original line number Diff line number Diff line
@@ -19,9 +19,12 @@ import android.app.ActivityThread;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.Signature;
import android.database.Cursor;
import android.graphics.Typeface;
import android.graphics.fonts.FontRequest;
import android.graphics.fonts.FontResult;
import android.net.Uri;
@@ -34,9 +37,13 @@ import android.os.ResultReceiver;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Utility class to deal with Font ContentProviders.
@@ -107,6 +114,13 @@ public class FontsContract {
        mPackageManager = mContext.getPackageManager();
    }

    /** @hide */
    @VisibleForTesting
    public FontsContract(Context context, PackageManager packageManager) {
        mContext = context;
        mPackageManager = packageManager;
    }

    // We use a background thread to post the content resolving work for all requests on. This
    // thread should be quit/stopped after all requests are done.
    private final Runnable mReplaceDispatcherThreadRunnable = new Runnable() {
@@ -133,31 +147,79 @@ public class FontsContract {
                mHandler = new Handler(mThread.getLooper());
            }
            mHandler.post(() -> {
                String providerAuthority = request.getProviderAuthority();
                // TODO: Implement cert checking for non-system apps
                ProviderInfo providerInfo = mPackageManager.resolveContentProvider(
                        providerAuthority, PackageManager.MATCH_SYSTEM_ONLY);
                ProviderInfo providerInfo = getProvider(request);
                if (providerInfo == null) {
                    receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null);
                    return;
                }
                Bundle result = getFontFromProvider(request, receiver, providerInfo);
                if (result == null) {
                    receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
                    return;
                }
                receiver.send(RESULT_CODE_OK, result);
                getFontFromProvider(request, receiver, providerInfo.authority);
            });
            mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable);
            mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS);
        }
    }

    private Bundle getFontFromProvider(FontRequest request, ResultReceiver receiver,
            ProviderInfo providerInfo) {
    /** @hide */
    @VisibleForTesting
    public ProviderInfo getProvider(FontRequest request) {
        String providerAuthority = request.getProviderAuthority();
        ProviderInfo info = mPackageManager.resolveContentProvider(providerAuthority, 0);
        if (info == null) {
            Log.e(TAG, "Can't find content provider " + providerAuthority);
            return null;
        }

        if (!info.packageName.equals(request.getProviderPackage())) {
            Log.e(TAG, "Found content provider " + providerAuthority + ", but package was not "
                    + request.getProviderPackage());
            return null;
        }
        // Trust system apps without signature checks
        if (info.applicationInfo.isSystemApp()) {
            return info;
        }

        Set<byte[]> signatures;
        try {
            PackageInfo packageInfo = mPackageManager.getPackageInfo(info.packageName,
                    PackageManager.GET_SIGNATURES);
            signatures = convertToSet(packageInfo.signatures);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Can't find content provider " + providerAuthority, e);
            return null;
        }
        List<List<byte[]>> requestCertificatesList = request.getCertificates();
        for (int i = 0; i < requestCertificatesList.size(); ++i) {
            final Set<byte[]> requestCertificates = convertToSet(requestCertificatesList.get(i));
            if (signatures.equals(requestCertificates)) {
                return info;
            }
        }
        Log.e(TAG, "Certificates don't match for given provider " + providerAuthority);
        return null;
    }

    private Set<byte[]> convertToSet(Signature[] signatures) {
        Set<byte[]> shas = new HashSet<>();
        for (int i = 0; i < signatures.length; ++i) {
            shas.add(signatures[i].toByteArray());
        }
        return shas;
    }

    private Set<byte[]> convertToSet(List<byte[]> certs) {
        Set<byte[]> shas = new HashSet<>();
        shas.addAll(certs);
        return shas;
    }

    /** @hide */
    @VisibleForTesting
    public void getFontFromProvider(FontRequest request, ResultReceiver receiver,
            String authority) {
        ArrayList<FontResult> result = null;
        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                .authority(providerInfo.authority)
                .authority(authority)
                .build();
        try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID,
                        Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE },
@@ -176,13 +238,16 @@ public class FontsContract {
                    try {
                        ParcelFileDescriptor pfd =
                                mContext.getContentResolver().openFileDescriptor(fileUri, "r");
                        final int ttcIndex = cursor.getInt(ttcIndexColumnIndex);
                        final String variationSettings = cursor.getString(vsColumnIndex);
                        final int style = cursor.getInt(styleColumnIndex);
                        final int ttcIndex = ttcIndexColumnIndex != -1
                                ? cursor.getInt(ttcIndexColumnIndex) : 0;
                        final String variationSettings = vsColumnIndex != -1
                                ? cursor.getString(vsColumnIndex) : null;
                        final int style = styleColumnIndex != -1
                                ? cursor.getInt(styleColumnIndex) : Typeface.NORMAL;
                        result.add(new FontResult(pfd, ttcIndex, variationSettings, style));
                    } catch (FileNotFoundException e) {
                        Log.e(TAG, "FileNotFoundException raised when interacting with content "
                                + "provider " + providerInfo.authority, e);
                                + "provider " + authority, e);
                    }
                }
            }
@@ -190,8 +255,9 @@ public class FontsContract {
        if (result != null && !result.isEmpty()) {
            Bundle bundle = new Bundle();
            bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result);
            return bundle;
            receiver.send(RESULT_CODE_OK, bundle);
            return;
        }
        return null;
        receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
    }
}
Loading