Merge "Cache the Typeface based on the FontRequest." into oc-dev
am: 5b96e55d90
Change-Id: Ic185a25c605e7345afa214d3b50fd859135f7b9b
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 3fa92b8..068628a 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -44,6 +44,7 @@
import android.os.Process;
import android.os.ResultReceiver;
import android.util.Log;
+import android.util.LruCache;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -181,6 +182,8 @@
@GuardedBy("mLock")
private HandlerThread mThread;
+ private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
+
/** @hide */
public FontsContract(Context context) {
mContext = context.getApplicationContext();
@@ -476,6 +479,11 @@
* therefore the result is delivered to the given callback. See {@link FontRequest}.
* Only one of the methods in callback will be invoked, depending on whether the request
* succeeds or fails. These calls will happen on the caller thread.
+ *
+ * Note that the result Typeface may be cached internally and the same instance will be returned
+ * the next time you call this method with the same request. If you want to bypass this cache,
+ * use {@link #fetchFonts} and {@link #buildTypeface} instead.
+ *
* @param context A context to be used for fetching from font provider.
* @param request A {@link FontRequest} object that identifies the provider and query for the
* request. May not be null.
@@ -486,8 +494,13 @@
@NonNull FontRequestCallback callback, @NonNull Handler handler) {
final Handler callerThreadHandler = new Handler();
+ final Typeface cachedTypeface = sTypefaceCache.get(request.getIdentifier());
+ if (cachedTypeface != null) {
+ callerThreadHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
+ return;
+ }
+
handler.post(() -> {
- // TODO: Cache the result.
FontFamilyResult result;
try {
result = fetchFonts(context, null /* cancellation signal */, request);
@@ -497,6 +510,13 @@
return;
}
+ // Same request might be dispatched during fetchFonts. Check the cache again.
+ final Typeface anotherCachedTypeface = sTypefaceCache.get(request.getIdentifier());
+ if (anotherCachedTypeface != null) {
+ callerThreadHandler.post(() -> callback.onTypefaceRetrieved(anotherCachedTypeface));
+ return;
+ }
+
if (result.getStatusCode() != FontFamilyResult.STATUS_OK) {
switch (result.getStatusCode()) {
case FontFamilyResult.STATUS_WRONG_CERTIFICATES:
@@ -547,6 +567,7 @@
return;
}
+ sTypefaceCache.put(request.getIdentifier(), typeface);
callerThreadHandler.post(() -> callback.onTypefaceRetrieved(typeface));
});
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 5669189..94a515b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1266,6 +1266,11 @@
<meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" />
</provider>
+ <provider android:name="android.provider.MockFontProvider"
+ android:authorities="android.provider.fonts.font"
+ android:exported="false"
+ android:multiprocess="true" />
+
<!-- Application components used for content tests -->
<provider android:name="android.content.MemoryFileProvider"
android:authorities="android.content.MemoryFileProvider"
diff --git a/core/tests/coretests/assets/fonts/samplefont1.ttf b/core/tests/coretests/assets/fonts/samplefont1.ttf
new file mode 100644
index 0000000..020436a
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/samplefont1.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/samplefont1.ttx b/core/tests/coretests/assets/fonts/samplefont1.ttx
new file mode 100644
index 0000000..40fa268
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/samplefont1.ttx
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
new file mode 100644
index 0000000..479f6dd
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 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 android.provider;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertNotSame;
+
+import android.app.Instrumentation;
+import android.content.pm.Signature;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageInfo;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.graphics.fonts.FontRequest;
+import android.provider.FontsContract;
+import android.provider.FontsContract.FontFamilyResult;
+import android.provider.FontsContract.FontInfo;
+import android.provider.FontsContract.Columns;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.os.Handler;
+import java.util.List;
+import java.util.ArrayList;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontsContractE2ETest {
+ private static final String AUTHORITY = "android.provider.fonts.font";
+ private static final String PACKAGE = "com.android.frameworks.coretests";
+
+ // Signature to be used for authentication to access content provider.
+ // In this test case, the content provider and consumer live in the same package, self package's
+ // signature works.
+ private static List<List<byte[]>> SIGNATURE;
+ static {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ try {
+ PackageManager manager = context.getPackageManager();
+ PackageInfo info = manager.getPackageInfo(
+ context.getPackageName(), PackageManager.GET_SIGNATURES);
+ ArrayList<byte[]> out = new ArrayList<>();
+ for (Signature sig : info.signatures) {
+ out.add(sig.toByteArray());
+ }
+ SIGNATURE = new ArrayList<>();
+ SIGNATURE.add(out);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockFontProvider.prepareFontFiles(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ }
+
+ @After
+ public void tearDown() {
+ MockFontProvider.cleanUpFontFiles(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ }
+
+ private static class TestCallback extends FontsContract.FontRequestCallback {
+ private Typeface mTypeface;
+
+ private int mSuccessCallCount;
+ private int mFailedCallCount;
+
+ public void onTypefaceRetrieved(Typeface typeface) {
+ mTypeface = typeface;
+ mSuccessCallCount++;
+ }
+
+ public void onTypefaceRequestFailed(int reason) {
+ mFailedCallCount++;
+ }
+
+ public Typeface getTypeface() {
+ return mTypeface;
+ }
+
+ public int getSuccessCallCount() {
+ return mSuccessCallCount;
+ }
+
+ public int getFailedCallCount() {
+ return mFailedCallCount;
+ }
+ }
+
+ @Test
+ public void typefaceCacheTest() throws NameNotFoundException {
+ Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ Context ctx = inst.getTargetContext();
+
+ final TestCallback callback = new TestCallback();
+ inst.runOnMainSync(() -> {
+ FontRequest request = new FontRequest(
+ AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE);
+ FontsContract.requestFont(ctx, request, callback, new Handler());
+ });
+ inst.waitForIdleSync();
+ assertEquals(1, callback.getSuccessCallCount());
+ assertEquals(0, callback.getFailedCallCount());
+ assertNotNull(callback.getTypeface());
+
+ final TestCallback callback2 = new TestCallback();
+ inst.runOnMainSync(() -> {
+ FontRequest request = new FontRequest(
+ AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE);
+ FontsContract.requestFont(ctx, request, callback2, new Handler());
+ });
+ inst.waitForIdleSync();
+ assertEquals(1, callback2.getSuccessCallCount());
+ assertEquals(0, callback2.getFailedCallCount());
+ assertSame(callback.getTypeface(), callback2.getTypeface());
+
+ final TestCallback callback3 = new TestCallback();
+ inst.runOnMainSync(() -> {
+ FontRequest request = new FontRequest(
+ AUTHORITY, PACKAGE, "singleFontFamily2", SIGNATURE);
+ FontsContract.requestFont(ctx, request, callback3, new Handler());
+ });
+ inst.waitForIdleSync();
+ assertEquals(1, callback3.getSuccessCallCount());
+ assertEquals(0, callback3.getFailedCallCount());
+ assertNotSame(callback.getTypeface(), callback3.getTypeface());
+ }
+
+ @Test
+ public void typefaceNotCacheTest() throws NameNotFoundException {
+ Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ Context ctx = inst.getTargetContext();
+
+ FontRequest request = new FontRequest(
+ AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE);
+ FontFamilyResult result = FontsContract.fetchFonts(
+ ctx, null /* cancellation signal */, request);
+ assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode());
+ Typeface typeface = FontsContract.buildTypeface(
+ ctx, null /* cancellation signal */, result.getFonts());
+
+ FontFamilyResult result2 = FontsContract.fetchFonts(
+ ctx, null /* cancellation signal */, request);
+ assertEquals(FontFamilyResult.STATUS_OK, result2.getStatusCode());
+ Typeface typeface2 = FontsContract.buildTypeface(
+ ctx, null /* cancellation signal */, result2.getFonts());
+
+ // Neighter fetchFonts nor buildTypeface should cache the Typeface.
+ assertNotSame(typeface, typeface2);
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/MockFontProvider.java b/core/tests/coretests/src/android/provider/MockFontProvider.java
new file mode 100644
index 0000000..339d5c3
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/MockFontProvider.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 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 android.provider;
+
+import static android.provider.FontsContract.Columns;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.fonts.FontVariationAxis;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.util.SparseArray;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.File;
+import java.nio.file.Files;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.nio.file.StandardCopyOption;
+
+public class MockFontProvider extends ContentProvider {
+ final static String AUTHORITY = "android.provider.fonts.font";
+
+ final static String[] FONT_FILES = {
+ "samplefont1.ttf",
+ };
+ private static final int SAMPLE_FONT_FILE_0_ID = 0;
+ private static final int SAMPLE_FONT_FILE_1_ID = 1;
+
+ static class Font {
+ public Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
+ int resultCode) {
+ mId = id;
+ mFileId = fileId;
+ mTtcIndex = ttcIndex;
+ mVarSettings = varSettings;
+ mWeight = weight;
+ mItalic = italic;
+ mResultCode = resultCode;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getTtcIndex() {
+ return mTtcIndex;
+ }
+
+ public String getVarSettings() {
+ return mVarSettings;
+ }
+
+ public int getWeight() {
+ return mWeight;
+ }
+
+ public int getItalic() {
+ return mItalic;
+ }
+
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ public int getFileId() {
+ return mFileId;
+ }
+
+ private int mId;
+ private int mFileId;
+ private int mTtcIndex;
+ private String mVarSettings;
+ private int mWeight;
+ private int mItalic;
+ private int mResultCode;
+ };
+
+ private static Map<String, Font[]> QUERY_MAP;
+ static {
+ HashMap<String, Font[]> map = new HashMap<>();
+ int id = 0;
+
+ map.put("singleFontFamily", new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK),
+ });
+
+ map.put("singleFontFamily2", new Font[] {
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK),
+ });
+
+ QUERY_MAP = Collections.unmodifiableMap(map);
+ }
+
+ private static Cursor buildCursor(Font[] in) {
+ MatrixCursor cursor = new MatrixCursor(new String[] {
+ Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.WEIGHT,
+ Columns.ITALIC, Columns.RESULT_CODE, Columns.FILE_ID});
+ for (Font font : in) {
+ MatrixCursor.RowBuilder builder = cursor.newRow();
+ builder.add(Columns._ID, font.getId());
+ builder.add(Columns.FILE_ID, font.getFileId());
+ builder.add(Columns.TTC_INDEX, font.getTtcIndex());
+ builder.add(Columns.VARIATION_SETTINGS, font.getVarSettings());
+ builder.add(Columns.WEIGHT, font.getWeight());
+ builder.add(Columns.ITALIC, font.getItalic());
+ builder.add(Columns.RESULT_CODE, font.getResultCode());
+ }
+ return cursor;
+ }
+
+ public MockFontProvider() {
+ }
+
+ public static void prepareFontFiles(Context context) {
+ final AssetManager mgr = context.getAssets();
+ for (String file : FONT_FILES) {
+ try (InputStream is = mgr.open("fonts/" + file)) {
+ Files.copy(is, getCopiedFile(context, file).toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static void cleanUpFontFiles(Context context) {
+ for (String file : FONT_FILES) {
+ getCopiedFile(context, file).delete();
+ }
+ }
+
+ public static File getCopiedFile(Context context, String path) {
+ return new File(context.getFilesDir(), path);
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) {
+ final int id = (int)ContentUris.parseId(uri);
+ final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]);
+ try {
+ return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(
+ "Failed to found font file. You might forget call prepareFontFiles in setUp");
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "vnd.android.cursor.dir/vnd.android.provider.font";
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return buildCursor(QUERY_MAP.get(selectionArgs[0]));
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("insert is not supported.");
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("delete is not supported.");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("update is not supported.");
+ }
+}
diff --git a/graphics/java/android/graphics/fonts/FontRequest.java b/graphics/java/android/graphics/fonts/FontRequest.java
index c7a5830..df3951c 100644
--- a/graphics/java/android/graphics/fonts/FontRequest.java
+++ b/graphics/java/android/graphics/fonts/FontRequest.java
@@ -35,6 +35,9 @@
private final String mQuery;
private final List<List<byte[]>> mCertificates;
+ // Used for key of the cache.
+ private final String mIdentifier;
+
/**
* @param providerAuthority The authority of the Font Provider to be used for the request. This
* should be a system installed app.
@@ -49,6 +52,8 @@
mQuery = Preconditions.checkNotNull(query);
mProviderPackage = Preconditions.checkNotNull(providerPackage);
mCertificates = Collections.emptyList();
+ mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage)
+ .append("-").append(mQuery).toString();
}
/**
@@ -68,6 +73,8 @@
mProviderPackage = Preconditions.checkNotNull(providerPackage);
mQuery = Preconditions.checkNotNull(query);
mCertificates = Preconditions.checkNotNull(certificates);
+ mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage)
+ .append("-").append(mQuery).toString();
}
/**
@@ -102,6 +109,11 @@
return mCertificates;
}
+ /** @hide */
+ public String getIdentifier() {
+ return mIdentifier;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -121,6 +133,8 @@
mQuery = in.readString();
mCertificates = new ArrayList<>();
in.readList(mCertificates, null);
+ mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage)
+ .append("-").append(mQuery).toString();
}
public static final Parcelable.Creator<FontRequest> CREATOR =