Merge "XML Support for non system font providers"
This commit is contained in:
committed by
Android (Google) Code Review
commit
1deac6a106
@@ -608,6 +608,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 fontProviderCerts = 16844128; // 0x1010560
|
||||
field public static final int fontProviderPackage = 16844122; // 0x101055a
|
||||
field public static final int fontProviderQuery = 16844115; // 0x1010553
|
||||
field public static final int fontStyle = 16844095; // 0x101053f
|
||||
|
||||
@@ -721,6 +721,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 fontProviderCerts = 16844128; // 0x1010560
|
||||
field public static final int fontProviderPackage = 16844122; // 0x101055a
|
||||
field public static final int fontProviderQuery = 16844115; // 0x1010553
|
||||
field public static final int fontStyle = 16844095; // 0x101053f
|
||||
|
||||
@@ -608,6 +608,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 fontProviderCerts = 16844128; // 0x1010560
|
||||
field public static final int fontProviderPackage = 16844122; // 0x101055a
|
||||
field public static final int fontProviderQuery = 16844115; // 0x1010553
|
||||
field public static final int fontStyle = 16844095; // 0x101053f
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -44,12 +45,14 @@ public class FontResourcesParser {
|
||||
private final @NonNull String mProviderAuthority;
|
||||
private final @NonNull String mProviderPackage;
|
||||
private final @NonNull String mQuery;
|
||||
private final @Nullable List<List<String>> mCerts;
|
||||
|
||||
public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg,
|
||||
@NonNull String query) {
|
||||
@NonNull String query, @Nullable List<List<String>> certs) {
|
||||
mProviderAuthority = authority;
|
||||
mProviderPackage = pkg;
|
||||
mQuery = query;
|
||||
mCerts = certs;
|
||||
}
|
||||
|
||||
public @NonNull String getAuthority() {
|
||||
@@ -63,6 +66,10 @@ public class FontResourcesParser {
|
||||
public @NonNull String getQuery() {
|
||||
return mQuery;
|
||||
}
|
||||
|
||||
public @Nullable List<List<String>> getCerts() {
|
||||
return mCerts;
|
||||
}
|
||||
}
|
||||
|
||||
// A class represents font element in xml file which points a file in resource.
|
||||
@@ -144,12 +151,33 @@ public class FontResourcesParser {
|
||||
String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
|
||||
String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
|
||||
String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
|
||||
int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0);
|
||||
array.recycle();
|
||||
if (authority != null && providerPackage != null && query != null) {
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
skip(parser);
|
||||
}
|
||||
return new ProviderResourceEntry(authority, providerPackage, query);
|
||||
List<List<String>> certs = null;
|
||||
if (certsId != 0) {
|
||||
TypedArray typedArray = resources.obtainTypedArray(certsId);
|
||||
if (typedArray.length() > 0) {
|
||||
certs = new ArrayList<>();
|
||||
boolean isArrayOfArrays = typedArray.getResourceId(0, 0) != 0;
|
||||
if (isArrayOfArrays) {
|
||||
for (int i = 0; i < typedArray.length(); i++) {
|
||||
int certId = typedArray.getResourceId(i, 0);
|
||||
String[] certsArray = resources.getStringArray(certId);
|
||||
List<String> certsList = Arrays.asList(certsArray);
|
||||
certs.add(certsList);
|
||||
}
|
||||
} else {
|
||||
String[] certsArray = resources.getStringArray(certsId);
|
||||
List<String> certsList = Arrays.asList(certsArray);
|
||||
certs.add(certsList);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ProviderResourceEntry(authority, providerPackage, query, certs);
|
||||
}
|
||||
List<FontFileResourceEntry> fonts = new ArrayList<>();
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package android.provider;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
@@ -42,9 +41,10 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility class to deal with Font ContentProviders.
|
||||
@@ -207,11 +207,12 @@ public class FontsContract {
|
||||
return info;
|
||||
}
|
||||
|
||||
Set<byte[]> signatures;
|
||||
List<byte[]> signatures;
|
||||
try {
|
||||
PackageInfo packageInfo = mPackageManager.getPackageInfo(info.packageName,
|
||||
PackageManager.GET_SIGNATURES);
|
||||
signatures = convertToSet(packageInfo.signatures);
|
||||
signatures = convertToByteArrayList(packageInfo.signatures);
|
||||
Collections.sort(signatures, sByteArrayComparator);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Can't find content provider " + providerAuthority, e);
|
||||
receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null);
|
||||
@@ -219,8 +220,10 @@ public class FontsContract {
|
||||
}
|
||||
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)) {
|
||||
// Make a copy so we can sort it without modifying the incoming data.
|
||||
List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i));
|
||||
Collections.sort(requestSignatures, sByteArrayComparator);
|
||||
if (equalsByteArrayList(signatures, requestSignatures)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -229,20 +232,38 @@ public class FontsContract {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Set<byte[]> convertToSet(Signature[] signatures) {
|
||||
Set<byte[]> shas = new HashSet<>();
|
||||
private static final Comparator<byte[]> sByteArrayComparator = (l, r) -> {
|
||||
if (l.length != r.length) {
|
||||
return l.length - r.length;
|
||||
}
|
||||
for (int i = 0; i < l.length; ++i) {
|
||||
if (l[i] != r[i]) {
|
||||
return l[i] - r[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
private boolean equalsByteArrayList(List<byte[]> signatures, List<byte[]> requestSignatures) {
|
||||
if (signatures.size() != requestSignatures.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < signatures.size(); ++i) {
|
||||
if (!Arrays.equals(signatures.get(i), requestSignatures.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<byte[]> convertToByteArrayList(Signature[] signatures) {
|
||||
List<byte[]> shas = new ArrayList<>();
|
||||
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,
|
||||
|
||||
@@ -8569,6 +8569,7 @@
|
||||
<attr name="fontProviderAuthority" format="string" />
|
||||
<attr name="fontProviderPackage" format="string" />
|
||||
<attr name="fontProviderQuery" format="string" />
|
||||
<attr name="fontProviderCerts" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- @hide -->
|
||||
|
||||
@@ -2809,6 +2809,7 @@
|
||||
<public name="isStatic" />
|
||||
<public name="isFeatureSplit" />
|
||||
<public name="singleLineTitle" />
|
||||
<public name="fontProviderCerts" />
|
||||
</public-group>
|
||||
|
||||
<public-group type="style" first-id="0x010302e0">
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fontProviderAuthority="com.example.test.fontprovider"
|
||||
android:fontProviderQuery="MyRequestedFont"
|
||||
android:fontProviderPackage="com.example.test.fontprovider.package"
|
||||
android:fontProviderCerts="@array/certarray">
|
||||
</font-family>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fontProviderAuthority="com.example.test.fontprovider"
|
||||
android:fontProviderQuery="MyRequestedFont"
|
||||
android:fontProviderPackage="com.example.test.fontprovider.package"
|
||||
android:fontProviderCerts="@array/certs1">
|
||||
</font-family>
|
||||
@@ -31,4 +31,19 @@
|
||||
<item>2 days</item>
|
||||
<item>1 week</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="certs1">
|
||||
<item>123456789</item>
|
||||
<item>987654321</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="certs2">
|
||||
<item>abcdefg</item>
|
||||
<item>gfedcba</item>
|
||||
</string-array>
|
||||
|
||||
<array name="certarray">
|
||||
<item>@array/certs1</item>
|
||||
<item>@array/certs2</item>
|
||||
</array>
|
||||
</resources>
|
||||
|
||||
@@ -15,21 +15,20 @@
|
||||
*/
|
||||
package android.content.res;
|
||||
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static android.content.res.FontResourcesParser.FamilyResourceEntry;
|
||||
import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
|
||||
import static android.content.res.FontResourcesParser.FontFileResourceEntry;
|
||||
import static android.content.res.FontResourcesParser.ProviderResourceEntry;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import static android.content.res.FontResourcesParser.FamilyResourceEntry;
|
||||
import static android.content.res.FontResourcesParser.ProviderResourceEntry;
|
||||
import static android.content.res.FontResourcesParser.FontFileResourceEntry;
|
||||
import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.FontConfig;
|
||||
|
||||
import com.android.frameworks.coretests.R;
|
||||
|
||||
@@ -97,4 +96,50 @@ public class FontResourcesParserTest {
|
||||
assertEquals("com.example.test.fontprovider.package", providerEntry.getPackage());
|
||||
assertEquals("MyRequestedFont", providerEntry.getQuery());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseDownloadableFont_singleCerts() throws IOException, XmlPullParserException {
|
||||
XmlResourceParser parser = mResources.getXml(R.font.samplexmldownloadedfontsinglecerts);
|
||||
|
||||
FamilyResourceEntry result = FontResourcesParser.parse(parser, mResources);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result instanceof ProviderResourceEntry);
|
||||
ProviderResourceEntry providerResourceEntry = (ProviderResourceEntry) result;
|
||||
assertEquals("com.example.test.fontprovider", providerResourceEntry.getAuthority());
|
||||
assertEquals("MyRequestedFont", providerResourceEntry.getQuery());
|
||||
assertEquals("com.example.test.fontprovider.package", providerResourceEntry.getPackage());
|
||||
List<List<String>> certList = providerResourceEntry.getCerts();
|
||||
assertNotNull(certList);
|
||||
assertEquals(1, certList.size());
|
||||
List<String> certs = certList.get(0);
|
||||
assertEquals(2, certs.size());
|
||||
assertEquals("123456789", certs.get(0));
|
||||
assertEquals("987654321", certs.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseDownloadableFont_multipleCerts() throws IOException, XmlPullParserException {
|
||||
XmlResourceParser parser = mResources.getXml(R.font.samplexmldownloadedfontmulticerts);
|
||||
|
||||
FamilyResourceEntry result = FontResourcesParser.parse(parser, mResources);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result instanceof ProviderResourceEntry);
|
||||
ProviderResourceEntry providerResourceEntry = (ProviderResourceEntry) result;
|
||||
assertEquals("com.example.test.fontprovider", providerResourceEntry.getAuthority());
|
||||
assertEquals("MyRequestedFont", providerResourceEntry.getQuery());
|
||||
assertEquals("com.example.test.fontprovider.package", providerResourceEntry.getPackage());
|
||||
List<List<String>> certList = providerResourceEntry.getCerts();
|
||||
assertNotNull(certList);
|
||||
assertEquals(2, certList.size());
|
||||
List<String> certs1 = certList.get(0);
|
||||
assertEquals(2, certs1.size());
|
||||
assertEquals("123456789", certs1.get(0));
|
||||
assertEquals("987654321", certs1.get(1));
|
||||
List<String> certs2 = certList.get(1);
|
||||
assertEquals(2, certs2.size());
|
||||
assertEquals("abcdefg", certs2.get(0));
|
||||
assertEquals("gfedcba", certs2.get(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ import java.util.List;
|
||||
public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> {
|
||||
private static final byte[] BYTE_ARRAY =
|
||||
Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
|
||||
// Use a different instance to test byte array comparison
|
||||
private static final byte[] BYTE_ARRAY_COPY =
|
||||
Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
|
||||
private static final byte[] BYTE_ARRAY_2 =
|
||||
Base64.decode("e04fd020ea3a6910a2d808002b32", Base64.DEFAULT);
|
||||
private static final String PACKAGE_NAME = "com.my.font.provider.package";
|
||||
|
||||
private final FontRequest request = new FontRequest(
|
||||
@@ -268,6 +273,34 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> {
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
public void testGetProvider_providerIsNonSystemAppDuplicateCerts()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
ProviderInfo info = new ProviderInfo();
|
||||
info.packageName = PACKAGE_NAME;
|
||||
info.applicationInfo = new ApplicationInfo();
|
||||
when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info);
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
Signature signature = mock(Signature.class);
|
||||
when(signature.toByteArray()).thenReturn(BYTE_ARRAY_COPY);
|
||||
Signature signature2 = mock(Signature.class);
|
||||
when(signature2.toByteArray()).thenReturn(BYTE_ARRAY_COPY);
|
||||
packageInfo.packageName = PACKAGE_NAME;
|
||||
packageInfo.signatures = new Signature[] { signature, signature2 };
|
||||
when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
|
||||
|
||||
// The provider has {BYTE_ARRAY_COPY, BYTE_ARRAY_COPY}, the request has
|
||||
// {BYTE_ARRAY_2, BYTE_ARRAY_COPY}.
|
||||
List<byte[]> certList = Arrays.asList(BYTE_ARRAY_2, BYTE_ARRAY_COPY);
|
||||
FontRequest requestRightCerts = new FontRequest(
|
||||
TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
|
||||
ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver);
|
||||
|
||||
// The given list includes an extra cert and doesn't have a second copy of the cert like
|
||||
// the provider does, so it should have failed.
|
||||
verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
public void testGetProvider_providerIsNonSystemAppCorrectCertsSeveralSets()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
ProviderInfo info = setupPackageManager();
|
||||
@@ -306,7 +339,7 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> {
|
||||
when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info);
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
Signature signature = mock(Signature.class);
|
||||
when(signature.toByteArray()).thenReturn(BYTE_ARRAY);
|
||||
when(signature.toByteArray()).thenReturn(BYTE_ARRAY_COPY);
|
||||
packageInfo.packageName = PACKAGE_NAME;
|
||||
packageInfo.signatures = new Signature[] { signature };
|
||||
when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.os.ResultReceiver;
|
||||
import android.provider.FontsContract;
|
||||
import android.text.FontConfig;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.LruCache;
|
||||
@@ -196,10 +197,22 @@ public class Typeface {
|
||||
if (typeface != null) {
|
||||
return typeface;
|
||||
}
|
||||
List<List<String>> givenCerts = providerEntry.getCerts();
|
||||
List<List<byte[]>> certs = new ArrayList<>();
|
||||
if (givenCerts != null) {
|
||||
for (int i = 0; i < givenCerts.size(); i++) {
|
||||
List<String> certSet = givenCerts.get(i);
|
||||
List<byte[]> byteArraySet = new ArrayList<>();
|
||||
for (int j = 0; j < certSet.size(); j++) {
|
||||
byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
|
||||
}
|
||||
certs.add(byteArraySet);
|
||||
}
|
||||
}
|
||||
// Downloaded font and it wasn't cached, request it again and return a
|
||||
// default font instead (nothing we can do now).
|
||||
create(new FontRequest(providerEntry.getAuthority(), providerEntry.getPackage(),
|
||||
providerEntry.getQuery()), NO_OP_REQUEST_CALLBACK);
|
||||
providerEntry.getQuery(), certs), NO_OP_REQUEST_CALLBACK);
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user