Loading api/current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -13718,9 +13718,12 @@ package android.graphics { public static abstract interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1 field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0 field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { Loading Loading @@ -34217,6 +34220,11 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings"; api/system-current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -14454,9 +14454,12 @@ package android.graphics { public static abstract interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1 field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0 field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { Loading Loading @@ -37121,6 +37124,11 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings"; api/test-current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -13756,9 +13756,12 @@ package android.graphics { public static abstract interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1 field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0 field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { Loading Loading @@ -34344,6 +34347,11 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings"; core/java/android/provider/FontsContract.java +68 −14 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import com.android.internal.annotations.GuardedBy; 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.List; Loading Loading @@ -78,6 +79,37 @@ public class FontsContract { * {@link android.graphics.Typeface#BOLD_ITALIC} */ public static final String STYLE = "font_style"; /** * Constant used to request data from a font provider. The cursor returned from the query * should have this column populated to indicate the result status of the * query. This will be checked before any other data in the cursor. Possible values are * {@link #RESULT_CODE_OK}, {@link #RESULT_CODE_FONT_NOT_FOUND}, * {@link #RESULT_CODE_MALFORMED_QUERY} and {@link #RESULT_CODE_FONT_UNAVAILABLE}. If not * present, {@link #RESULT_CODE_OK} will be assumed. */ public static final String RESULT_CODE = "result_code"; /** * Constant used to represent a result was retrieved successfully. The given fonts will be * attempted to retrieve immediately via * {@link android.content.ContentProvider#openFile(Uri, String)}. See {@link #RESULT_CODE}. */ public static final int RESULT_CODE_OK = 0; /** * Constant used to represent a result was not found. See {@link #RESULT_CODE}. */ public static final int RESULT_CODE_FONT_NOT_FOUND = 1; /** * Constant used to represent a result was found, but cannot be provided at this moment. Use * this to indicate, for example, that a font needs to be fetched from the network. See * {@link #RESULT_CODE}. */ public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; /** * Constant used to represent that the query was not in a supported format by the provider. * See {@link #RESULT_CODE}. */ public static final int RESULT_CODE_MALFORMED_QUERY = 3; } /** Loading @@ -87,12 +119,13 @@ public class FontsContract { */ public static final String PARCEL_FONT_RESULTS = "font_results"; // Error codes internal to the system, which can not come from a provider. To keep the number // space open for new provider codes, these should all be negative numbers. /** @hide */ public static final int RESULT_CODE_OK = 0; public static final int RESULT_CODE_PROVIDER_NOT_FOUND = -1; /** @hide */ public static final int RESULT_CODE_FONT_NOT_FOUND = 1; /** @hide */ public static final int RESULT_CODE_PROVIDER_NOT_FOUND = 2; public static final int RESULT_CODE_WRONG_CERTIFICATES = -2; // Note -3 is used by Typeface to indicate the font failed to load. private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000; Loading Loading @@ -136,9 +169,7 @@ public class FontsContract { } }; /** * @hide */ /** @hide */ public void getFont(FontRequest request, ResultReceiver receiver) { synchronized (mLock) { if (mHandler == null) { Loading @@ -147,9 +178,8 @@ public class FontsContract { mHandler = new Handler(mThread.getLooper()); } mHandler.post(() -> { ProviderInfo providerInfo = getProvider(request); ProviderInfo providerInfo = getProvider(request, receiver); if (providerInfo == null) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; } getFontFromProvider(request, receiver, providerInfo.authority); Loading @@ -161,17 +191,19 @@ public class FontsContract { /** @hide */ @VisibleForTesting public ProviderInfo getProvider(FontRequest request) { public ProviderInfo getProvider(FontRequest request, ResultReceiver receiver) { String providerAuthority = request.getProviderAuthority(); ProviderInfo info = mPackageManager.resolveContentProvider(providerAuthority, 0); if (info == null) { Log.e(TAG, "Can't find content provider " + providerAuthority); receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return null; } if (!info.packageName.equals(request.getProviderPackage())) { Log.e(TAG, "Found content provider " + providerAuthority + ", but package was not " + request.getProviderPackage()); receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return null; } // Trust system apps without signature checks Loading @@ -186,6 +218,7 @@ public class FontsContract { signatures = convertToSet(packageInfo.signatures); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Can't find content provider " + providerAuthority, e); receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return null; } List<List<byte[]>> requestCertificatesList = request.getCertificates(); Loading @@ -196,6 +229,7 @@ public class FontsContract { } } Log.e(TAG, "Certificates don't match for given provider " + providerAuthority); receiver.send(RESULT_CODE_WRONG_CERTIFICATES, null); return null; } Loading @@ -222,17 +256,37 @@ public class FontsContract { .authority(authority) .build(); try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE }, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE, Columns.RESULT_CODE }, "query = ?", new String[] { request.getQuery() }, null);) { // TODO: Should we restrict the amount of fonts that can be returned? // TODO: Write documentation explaining that all results should be from the same family. if (cursor != null && cursor.getCount() > 0) { final int resultCodeColumnIndex = cursor.getColumnIndex(Columns.RESULT_CODE); int resultCode = -1; result = new ArrayList<>(); final int idColumnIndex = cursor.getColumnIndex(Columns._ID); final int idColumnIndex = cursor.getColumnIndexOrThrow(Columns._ID); final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX); final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS); final int styleColumnIndex = cursor.getColumnIndex(Columns.STYLE); while (cursor.moveToNext()) { resultCode = resultCodeColumnIndex != -1 ? cursor.getInt(resultCodeColumnIndex) : Columns.RESULT_CODE_OK; if (resultCode != Columns.RESULT_CODE_OK) { if (resultCode < 0) { // Negative values are reserved for the internal errors. resultCode = Columns.RESULT_CODE_FONT_NOT_FOUND; } for (int i = 0; i < result.size(); ++i) { try { result.get(i).getFileDescriptor().close(); } catch (IOException e) { // Ignore, as we are closing fds for cleanup. } } receiver.send(resultCode, null); return; } long id = cursor.getLong(idColumnIndex); Uri fileUri = ContentUris.withAppendedId(uri, id); try { Loading @@ -255,9 +309,9 @@ public class FontsContract { if (result != null && !result.isEmpty()) { Bundle bundle = new Bundle(); bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result); receiver.send(RESULT_CODE_OK, bundle); receiver.send(Columns.RESULT_CODE_OK, bundle); return; } receiver.send(RESULT_CODE_FONT_NOT_FOUND, null); receiver.send(Columns.RESULT_CODE_FONT_NOT_FOUND, null); } } core/tests/coretests/src/android/provider/FontsContractTest.java +93 −13 Original line number Diff line number Diff line Loading @@ -15,12 +15,12 @@ */ package android.provider; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.pm.ApplicationInfo; Loading @@ -28,6 +28,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.Signature; import android.database.MatrixCursor; import android.graphics.Typeface; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; Loading Loading @@ -72,11 +73,12 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { mResultReceiver = mock(ResultReceiver.class); } public void testGetFontFromProvider() { public void testGetFontFromProvider_resultOK() { mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mResultReceiver).send(eq(FontsContract.RESULT_CODE_OK), bundleCaptor.capture()); verify(mResultReceiver).send( eq(FontsContract.Columns.RESULT_CODE_OK), bundleCaptor.capture()); Bundle bundle = bundleCaptor.getValue(); assertNotNull(bundle); Loading @@ -96,7 +98,8 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(eq(FontsContract.RESULT_CODE_OK), bundleCaptor.capture()); verify(mResultReceiver).send( eq(FontsContract.Columns.RESULT_CODE_OK), bundleCaptor.capture()); Bundle bundle = bundleCaptor.getValue(); assertNotNull(bundle); Loading @@ -111,11 +114,79 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { assertNotNull(fontResult.getFileDescriptor()); } public void testGetFontFromProvider_resultFontNotFound() { // Make the provider return unknown mProvider.setResultCode(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND,null); } public void testGetFontFromProvider_resultFontUnavailable() { // Make the provider return font unavailable mProvider.setResultCode(FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE,null); } public void testGetFontFromProvider_resultMalformedQuery() { // Make the provider return font unavailable mProvider.setResultCode(FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY,null); } public void testGetFontFromProvider_resultFontNotFoundSecondRow() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND, null); } public void testGetFontFromProvider_resultFontNotFoundOtherRow() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND, null); } public void testGetFontFromProvider_resultCodeIsNegativeNumber() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, -5}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND, null); } public void testGetProvider_providerNotFound() { when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(null); ProviderInfo result = mContract.getProvider(request); ProviderInfo result = mContract.getProvider(request, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND, null); assertNull(result); } Loading @@ -124,8 +195,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { info.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info); ProviderInfo result = mContract.getProvider(request); ProviderInfo result = mContract.getProvider(request, mResultReceiver); verifyZeroInteractions(mResultReceiver); assertEquals(info, result); } Loading @@ -136,8 +208,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info); ProviderInfo result = mContract.getProvider( new FontRequest(TestFontsProvider.AUTHORITY, "com.wrong.package", "query")); new FontRequest(TestFontsProvider.AUTHORITY, "com.wrong.package", "query"), mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND, null); assertNull(result); } Loading @@ -146,8 +220,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { setupPackageManager(); // The default request is missing the certificates info. ProviderInfo result = mContract.getProvider(request); ProviderInfo result = mContract.getProvider(request, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null); assertNull(result); } Loading @@ -159,8 +234,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { List<byte[]> certList = Arrays.asList(wrongCert); FontRequest requestWrongCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList)); ProviderInfo result = mContract.getProvider(requestWrongCerts); ProviderInfo result = mContract.getProvider(requestWrongCerts, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null); assertNull(result); } Loading @@ -171,8 +247,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { List<byte[]> certList = Arrays.asList(BYTE_ARRAY); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList)); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); verifyZeroInteractions(mResultReceiver); assertEquals(info, result); } Loading @@ -184,9 +261,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { List<byte[]> certList = Arrays.asList(wrongCert, BYTE_ARRAY); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList)); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); // There is one too many certs, should fail as the set doesn't match. verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null); assertNull(result); } Loading @@ -200,8 +278,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { certList.add(Arrays.asList(BYTE_ARRAY)); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", certList); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); verifyZeroInteractions(mResultReceiver); assertEquals(info, result); } Loading @@ -213,8 +292,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { certList.add(Arrays.asList(BYTE_ARRAY)); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, "com.wrong.package.name", "query", certList); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND, null); assertNull(result); } Loading Loading
api/current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -13718,9 +13718,12 @@ package android.graphics { public static abstract interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1 field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0 field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { Loading Loading @@ -34217,6 +34220,11 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
api/system-current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -14454,9 +14454,12 @@ package android.graphics { public static abstract interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1 field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0 field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { Loading Loading @@ -37121,6 +37124,11 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
api/test-current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -13756,9 +13756,12 @@ package android.graphics { public static abstract interface Typeface.FontRequestCallback { method public abstract void onTypefaceRequestFailed(int); method public abstract void onTypefaceRetrieved(android.graphics.Typeface); field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1 field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0 field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1 field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2 field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3 field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe } public class Xfermode { Loading Loading @@ -34344,6 +34347,11 @@ package android.provider { public static final class FontsContract.Columns implements android.provider.BaseColumns { ctor public FontsContract.Columns(); field public static final java.lang.String RESULT_CODE = "result_code"; field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1 field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2 field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3 field public static final int RESULT_CODE_OK = 0; // 0x0 field public static final java.lang.String STYLE = "font_style"; field public static final java.lang.String TTC_INDEX = "font_ttc_index"; field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
core/java/android/provider/FontsContract.java +68 −14 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import com.android.internal.annotations.GuardedBy; 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.List; Loading Loading @@ -78,6 +79,37 @@ public class FontsContract { * {@link android.graphics.Typeface#BOLD_ITALIC} */ public static final String STYLE = "font_style"; /** * Constant used to request data from a font provider. The cursor returned from the query * should have this column populated to indicate the result status of the * query. This will be checked before any other data in the cursor. Possible values are * {@link #RESULT_CODE_OK}, {@link #RESULT_CODE_FONT_NOT_FOUND}, * {@link #RESULT_CODE_MALFORMED_QUERY} and {@link #RESULT_CODE_FONT_UNAVAILABLE}. If not * present, {@link #RESULT_CODE_OK} will be assumed. */ public static final String RESULT_CODE = "result_code"; /** * Constant used to represent a result was retrieved successfully. The given fonts will be * attempted to retrieve immediately via * {@link android.content.ContentProvider#openFile(Uri, String)}. See {@link #RESULT_CODE}. */ public static final int RESULT_CODE_OK = 0; /** * Constant used to represent a result was not found. See {@link #RESULT_CODE}. */ public static final int RESULT_CODE_FONT_NOT_FOUND = 1; /** * Constant used to represent a result was found, but cannot be provided at this moment. Use * this to indicate, for example, that a font needs to be fetched from the network. See * {@link #RESULT_CODE}. */ public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; /** * Constant used to represent that the query was not in a supported format by the provider. * See {@link #RESULT_CODE}. */ public static final int RESULT_CODE_MALFORMED_QUERY = 3; } /** Loading @@ -87,12 +119,13 @@ public class FontsContract { */ public static final String PARCEL_FONT_RESULTS = "font_results"; // Error codes internal to the system, which can not come from a provider. To keep the number // space open for new provider codes, these should all be negative numbers. /** @hide */ public static final int RESULT_CODE_OK = 0; public static final int RESULT_CODE_PROVIDER_NOT_FOUND = -1; /** @hide */ public static final int RESULT_CODE_FONT_NOT_FOUND = 1; /** @hide */ public static final int RESULT_CODE_PROVIDER_NOT_FOUND = 2; public static final int RESULT_CODE_WRONG_CERTIFICATES = -2; // Note -3 is used by Typeface to indicate the font failed to load. private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000; Loading Loading @@ -136,9 +169,7 @@ public class FontsContract { } }; /** * @hide */ /** @hide */ public void getFont(FontRequest request, ResultReceiver receiver) { synchronized (mLock) { if (mHandler == null) { Loading @@ -147,9 +178,8 @@ public class FontsContract { mHandler = new Handler(mThread.getLooper()); } mHandler.post(() -> { ProviderInfo providerInfo = getProvider(request); ProviderInfo providerInfo = getProvider(request, receiver); if (providerInfo == null) { receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return; } getFontFromProvider(request, receiver, providerInfo.authority); Loading @@ -161,17 +191,19 @@ public class FontsContract { /** @hide */ @VisibleForTesting public ProviderInfo getProvider(FontRequest request) { public ProviderInfo getProvider(FontRequest request, ResultReceiver receiver) { String providerAuthority = request.getProviderAuthority(); ProviderInfo info = mPackageManager.resolveContentProvider(providerAuthority, 0); if (info == null) { Log.e(TAG, "Can't find content provider " + providerAuthority); receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return null; } if (!info.packageName.equals(request.getProviderPackage())) { Log.e(TAG, "Found content provider " + providerAuthority + ", but package was not " + request.getProviderPackage()); receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return null; } // Trust system apps without signature checks Loading @@ -186,6 +218,7 @@ public class FontsContract { signatures = convertToSet(packageInfo.signatures); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Can't find content provider " + providerAuthority, e); receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null); return null; } List<List<byte[]>> requestCertificatesList = request.getCertificates(); Loading @@ -196,6 +229,7 @@ public class FontsContract { } } Log.e(TAG, "Certificates don't match for given provider " + providerAuthority); receiver.send(RESULT_CODE_WRONG_CERTIFICATES, null); return null; } Loading @@ -222,17 +256,37 @@ public class FontsContract { .authority(authority) .build(); try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE }, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE, Columns.RESULT_CODE }, "query = ?", new String[] { request.getQuery() }, null);) { // TODO: Should we restrict the amount of fonts that can be returned? // TODO: Write documentation explaining that all results should be from the same family. if (cursor != null && cursor.getCount() > 0) { final int resultCodeColumnIndex = cursor.getColumnIndex(Columns.RESULT_CODE); int resultCode = -1; result = new ArrayList<>(); final int idColumnIndex = cursor.getColumnIndex(Columns._ID); final int idColumnIndex = cursor.getColumnIndexOrThrow(Columns._ID); final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX); final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS); final int styleColumnIndex = cursor.getColumnIndex(Columns.STYLE); while (cursor.moveToNext()) { resultCode = resultCodeColumnIndex != -1 ? cursor.getInt(resultCodeColumnIndex) : Columns.RESULT_CODE_OK; if (resultCode != Columns.RESULT_CODE_OK) { if (resultCode < 0) { // Negative values are reserved for the internal errors. resultCode = Columns.RESULT_CODE_FONT_NOT_FOUND; } for (int i = 0; i < result.size(); ++i) { try { result.get(i).getFileDescriptor().close(); } catch (IOException e) { // Ignore, as we are closing fds for cleanup. } } receiver.send(resultCode, null); return; } long id = cursor.getLong(idColumnIndex); Uri fileUri = ContentUris.withAppendedId(uri, id); try { Loading @@ -255,9 +309,9 @@ public class FontsContract { if (result != null && !result.isEmpty()) { Bundle bundle = new Bundle(); bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result); receiver.send(RESULT_CODE_OK, bundle); receiver.send(Columns.RESULT_CODE_OK, bundle); return; } receiver.send(RESULT_CODE_FONT_NOT_FOUND, null); receiver.send(Columns.RESULT_CODE_FONT_NOT_FOUND, null); } }
core/tests/coretests/src/android/provider/FontsContractTest.java +93 −13 Original line number Diff line number Diff line Loading @@ -15,12 +15,12 @@ */ package android.provider; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.pm.ApplicationInfo; Loading @@ -28,6 +28,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.Signature; import android.database.MatrixCursor; import android.graphics.Typeface; import android.graphics.fonts.FontRequest; import android.graphics.fonts.FontResult; Loading Loading @@ -72,11 +73,12 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { mResultReceiver = mock(ResultReceiver.class); } public void testGetFontFromProvider() { public void testGetFontFromProvider_resultOK() { mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(mResultReceiver).send(eq(FontsContract.RESULT_CODE_OK), bundleCaptor.capture()); verify(mResultReceiver).send( eq(FontsContract.Columns.RESULT_CODE_OK), bundleCaptor.capture()); Bundle bundle = bundleCaptor.getValue(); assertNotNull(bundle); Loading @@ -96,7 +98,8 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(eq(FontsContract.RESULT_CODE_OK), bundleCaptor.capture()); verify(mResultReceiver).send( eq(FontsContract.Columns.RESULT_CODE_OK), bundleCaptor.capture()); Bundle bundle = bundleCaptor.getValue(); assertNotNull(bundle); Loading @@ -111,11 +114,79 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { assertNotNull(fontResult.getFileDescriptor()); } public void testGetFontFromProvider_resultFontNotFound() { // Make the provider return unknown mProvider.setResultCode(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND,null); } public void testGetFontFromProvider_resultFontUnavailable() { // Make the provider return font unavailable mProvider.setResultCode(FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE,null); } public void testGetFontFromProvider_resultMalformedQuery() { // Make the provider return font unavailable mProvider.setResultCode(FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY,null); } public void testGetFontFromProvider_resultFontNotFoundSecondRow() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND, null); } public void testGetFontFromProvider_resultFontNotFoundOtherRow() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND, null); } public void testGetFontFromProvider_resultCodeIsNegativeNumber() { MatrixCursor cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID, FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS, FontsContract.Columns.STYLE, FontsContract.Columns.RESULT_CODE }); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, FontsContract.Columns.RESULT_CODE_OK}); cursor.addRow(new Object[] { 1, 0, null, Typeface.NORMAL, -5}); mProvider.setCustomCursor(cursor); mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY); verify(mResultReceiver).send(FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND, null); } public void testGetProvider_providerNotFound() { when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(null); ProviderInfo result = mContract.getProvider(request); ProviderInfo result = mContract.getProvider(request, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND, null); assertNull(result); } Loading @@ -124,8 +195,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { info.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info); ProviderInfo result = mContract.getProvider(request); ProviderInfo result = mContract.getProvider(request, mResultReceiver); verifyZeroInteractions(mResultReceiver); assertEquals(info, result); } Loading @@ -136,8 +208,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info); ProviderInfo result = mContract.getProvider( new FontRequest(TestFontsProvider.AUTHORITY, "com.wrong.package", "query")); new FontRequest(TestFontsProvider.AUTHORITY, "com.wrong.package", "query"), mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND, null); assertNull(result); } Loading @@ -146,8 +220,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { setupPackageManager(); // The default request is missing the certificates info. ProviderInfo result = mContract.getProvider(request); ProviderInfo result = mContract.getProvider(request, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null); assertNull(result); } Loading @@ -159,8 +234,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { List<byte[]> certList = Arrays.asList(wrongCert); FontRequest requestWrongCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList)); ProviderInfo result = mContract.getProvider(requestWrongCerts); ProviderInfo result = mContract.getProvider(requestWrongCerts, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null); assertNull(result); } Loading @@ -171,8 +247,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { List<byte[]> certList = Arrays.asList(BYTE_ARRAY); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList)); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); verifyZeroInteractions(mResultReceiver); assertEquals(info, result); } Loading @@ -184,9 +261,10 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { List<byte[]> certList = Arrays.asList(wrongCert, BYTE_ARRAY); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList)); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); // There is one too many certs, should fail as the set doesn't match. verify(mResultReceiver).send(FontsContract.RESULT_CODE_WRONG_CERTIFICATES, null); assertNull(result); } Loading @@ -200,8 +278,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { certList.add(Arrays.asList(BYTE_ARRAY)); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", certList); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); verifyZeroInteractions(mResultReceiver); assertEquals(info, result); } Loading @@ -213,8 +292,9 @@ public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> { certList.add(Arrays.asList(BYTE_ARRAY)); FontRequest requestRightCerts = new FontRequest( TestFontsProvider.AUTHORITY, "com.wrong.package.name", "query", certList); ProviderInfo result = mContract.getProvider(requestRightCerts); ProviderInfo result = mContract.getProvider(requestRightCerts, mResultReceiver); verify(mResultReceiver).send(FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND, null); assertNull(result); } Loading