1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.provider.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 25 import android.app.Instrumentation; 26 import android.content.Context; 27 import android.content.pm.PackageInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.PackageManager.NameNotFoundException; 30 import android.content.pm.Signature; 31 import android.graphics.Typeface; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.provider.FontRequest; 35 import android.provider.FontsContract; 36 import android.provider.FontsContract.Columns; 37 import android.provider.FontsContract.FontFamilyResult; 38 import android.provider.FontsContract.FontInfo; 39 40 import androidx.test.InstrumentationRegistry; 41 import androidx.test.filters.SmallTest; 42 import androidx.test.runner.AndroidJUnit4; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 import java.util.ArrayList; 50 import java.util.List; 51 52 @SmallTest 53 @RunWith(AndroidJUnit4.class) 54 public class FontsContractTest { 55 private static final String AUTHORITY = "android.provider.fonts.cts.font"; 56 private static final String PACKAGE = "android.provider.cts"; 57 58 // Signature to be used for authentication to access content provider. 59 // In this test case, the content provider and consumer live in the same package, self package's 60 // signature works. 61 private static List<List<byte[]>> SIGNATURE; 62 static { 63 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 64 try { 65 PackageManager manager = context.getPackageManager(); 66 PackageInfo info = manager.getPackageInfo( 67 context.getPackageName(), PackageManager.GET_SIGNATURES); 68 ArrayList<byte[]> out = new ArrayList<>(); 69 for (Signature sig : info.signatures) { sig.toByteArray()70 out.add(sig.toByteArray()); 71 } 72 SIGNATURE = new ArrayList<>(); 73 SIGNATURE.add(out); 74 } catch (PackageManager.NameNotFoundException e) { 75 throw new RuntimeException(e); 76 } 77 } 78 79 private Instrumentation mInstrumentation; 80 private Context mContext; 81 private Handler mMainThreadHandler; 82 83 @Before setUp()84 public void setUp() throws Exception { 85 mMainThreadHandler = new Handler(Looper.getMainLooper()); 86 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 87 mContext = mInstrumentation.getTargetContext(); 88 MockFontProvider.prepareFontFiles( 89 InstrumentationRegistry.getInstrumentation().getTargetContext()); 90 } 91 92 @After tearDown()93 public void tearDown() { 94 mMainThreadHandler = null; 95 MockFontProvider.cleanUpFontFiles( 96 InstrumentationRegistry.getInstrumentation().getTargetContext()); 97 } 98 99 private static class TestCallback extends FontsContract.FontRequestCallback { 100 private final Object mLock = new Object(); 101 // @GuardedBy("mLock") 102 private Typeface mTypeface; 103 // @GuardedBy("mLock") 104 private int mFailedReason; 105 // @GuardedBy("mLock") 106 private int mSuccessCallCount; 107 // @GuardedBy("mLock") 108 private int mFailedCallCount; 109 onTypefaceRetrieved(Typeface typeface)110 public void onTypefaceRetrieved(Typeface typeface) { 111 synchronized(mLock) { 112 mTypeface = typeface; 113 mSuccessCallCount++; 114 } 115 } 116 onTypefaceRequestFailed(int reason)117 public void onTypefaceRequestFailed(int reason) { 118 synchronized(mLock) { 119 mFailedCallCount++; 120 mFailedReason = reason; 121 } 122 } 123 getTypeface()124 public Typeface getTypeface() { 125 synchronized(mLock) { 126 return mTypeface; 127 } 128 } 129 getFailedReason()130 public int getFailedReason() { 131 synchronized(mLock) { 132 return mFailedReason; 133 } 134 } 135 getSuccessCallCount()136 public int getSuccessCallCount() { 137 synchronized(mLock) { 138 return mSuccessCallCount; 139 } 140 } 141 getFailedCallCount()142 public int getFailedCallCount() { 143 synchronized(mLock) { 144 return mFailedCallCount; 145 } 146 } 147 } 148 149 150 @Test requestFont()151 public void requestFont() throws NameNotFoundException { 152 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 153 MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE); 154 TestCallback callback = new TestCallback(); 155 156 mInstrumentation.runOnMainSync(() -> FontsContract.requestFonts( 157 mContext, request, mMainThreadHandler, null, callback)); 158 159 mInstrumentation.waitForIdleSync(); 160 assertEquals(1, callback.getSuccessCallCount()); 161 assertEquals(0, callback.getFailedCallCount()); 162 assertNotNull(callback.mTypeface); 163 } 164 165 @Test requestFontNegativeErrorCode()166 public void requestFontNegativeErrorCode() throws NameNotFoundException { 167 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 168 MockFontProvider.NEGATIVE_ERROR_CODE_QUERY, SIGNATURE); 169 TestCallback callback = new TestCallback(); 170 171 mInstrumentation.runOnMainSync(() -> FontsContract.requestFonts( 172 mContext, request, mMainThreadHandler, null, callback)); 173 174 mInstrumentation.waitForIdleSync(); 175 assertNull(callback.mTypeface); 176 assertEquals(1, callback.getFailedCallCount()); 177 assertEquals(0, callback.getSuccessCallCount()); 178 assertEquals(FontsContract.FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, 179 callback.getFailedReason()); 180 } 181 182 @Test querySingleFont()183 public void querySingleFont() throws NameNotFoundException { 184 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 185 MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE); 186 FontFamilyResult result = FontsContract.fetchFonts( 187 mContext, null /* cancellation signal */, request); 188 assertNotNull(result); 189 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 190 191 FontInfo[] fonts = result.getFonts(); 192 assertEquals(1, fonts.length); 193 FontInfo font = fonts[0]; 194 assertNotNull(font.getUri()); 195 assertEquals(Columns.RESULT_CODE_OK, font.getResultCode()); 196 assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 197 } 198 199 @Test queryMultipleFont()200 public void queryMultipleFont() throws NameNotFoundException { 201 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 202 MockFontProvider.MULTIPLE_FAMILY_QUERY, SIGNATURE); 203 FontFamilyResult result = FontsContract.fetchFonts( 204 mContext, null /* cancellation signal */, request); 205 assertNotNull(result); 206 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 207 208 FontInfo[] fonts = result.getFonts(); 209 assertEquals(4, fonts.length); 210 for (FontInfo font: fonts) { 211 assertNotNull(font.getUri()); 212 assertEquals(Columns.RESULT_CODE_OK, font.getResultCode()); 213 } 214 assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 215 } 216 217 @Test queryAttributes()218 public void queryAttributes() throws NameNotFoundException { 219 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 220 MockFontProvider.ALL_ATTRIBUTE_VALUES_QUERY, SIGNATURE); 221 FontFamilyResult result = FontsContract.fetchFonts( 222 mContext, null /* cancellation signal */, request); 223 assertNotNull(result); 224 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 225 226 FontInfo[] fonts = result.getFonts(); 227 assertEquals(2, fonts.length); 228 FontInfo font = fonts[0]; 229 assertNotNull(font.getUri()); 230 assertEquals(400, font.getWeight()); 231 assertEquals(1, font.getAxes().length); 232 assertEquals(0, font.getTtcIndex()); 233 assertFalse(font.isItalic()); 234 assertEquals(Columns.RESULT_CODE_OK, font.getResultCode()); 235 236 font = fonts[1]; 237 assertNotNull(font.getUri()); 238 assertEquals(700, font.getWeight()); 239 assertEquals(1, font.getAxes().length); 240 assertEquals(1, font.getTtcIndex()); 241 assertTrue(font.isItalic()); 242 assertEquals(Columns.RESULT_CODE_OK, font.getResultCode()); 243 244 assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 245 } 246 247 @Test queryNotFound()248 public void queryNotFound() throws NameNotFoundException { 249 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 250 MockFontProvider.NOT_FOUND_QUERY, SIGNATURE); 251 FontFamilyResult result = FontsContract.fetchFonts( 252 mContext, null /* cancellation signal */, request); 253 assertNotNull(result); 254 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 255 256 FontInfo[] fonts = result.getFonts(); 257 assertEquals(1, fonts.length); 258 FontInfo font = fonts[0]; 259 assertEquals(Columns.RESULT_CODE_FONT_NOT_FOUND, font.getResultCode()); 260 assertNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 261 } 262 263 @Test queryUnavailable()264 public void queryUnavailable() throws NameNotFoundException { 265 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 266 MockFontProvider.UNAVAILABLE_QUERY, SIGNATURE); 267 FontFamilyResult result = FontsContract.fetchFonts( 268 mContext, null /* cancellation signal */, request); 269 assertNotNull(result); 270 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 271 272 FontInfo[] fonts = result.getFonts(); 273 assertEquals(1, fonts.length); 274 FontInfo font = fonts[0]; 275 assertEquals(Columns.RESULT_CODE_FONT_UNAVAILABLE, font.getResultCode()); 276 assertNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 277 } 278 279 @Test queryMalformed()280 public void queryMalformed() throws NameNotFoundException { 281 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 282 MockFontProvider.MALFORMED_QUERY, SIGNATURE); 283 FontFamilyResult result = FontsContract.fetchFonts( 284 mContext, null /* cancellation signal */, request); 285 assertNotNull(result); 286 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 287 288 FontInfo[] fonts = result.getFonts(); 289 assertEquals(1, fonts.length); 290 FontInfo font = fonts[0]; 291 assertEquals(Columns.RESULT_CODE_MALFORMED_QUERY, font.getResultCode()); 292 assertNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 293 } 294 295 @Test queryMultipleOneNotFound()296 public void queryMultipleOneNotFound() throws NameNotFoundException { 297 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 298 MockFontProvider.NOT_FOUND_SECOND_QUERY, SIGNATURE); 299 FontFamilyResult result = FontsContract.fetchFonts( 300 mContext, null /* cancellation signal */, request); 301 assertNotNull(result); 302 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 303 304 FontInfo[] fonts = result.getFonts(); 305 assertEquals(2, fonts.length); 306 FontInfo font = fonts[0]; 307 assertEquals(Columns.RESULT_CODE_OK, font.getResultCode()); 308 FontInfo font2 = fonts[1]; 309 assertEquals(Columns.RESULT_CODE_FONT_NOT_FOUND, font2.getResultCode()); 310 assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 311 } 312 313 @Test queryMandatoryFieldsOnly()314 public void queryMandatoryFieldsOnly() throws NameNotFoundException { 315 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 316 MockFontProvider.MANDATORY_FIELDS_ONLY_QUERY, SIGNATURE); 317 FontFamilyResult result = FontsContract.fetchFonts( 318 mContext, null /* cancellation signal */, request); 319 assertNotNull(result); 320 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 321 322 FontInfo[] fonts = result.getFonts(); 323 assertEquals(1, fonts.length); 324 FontInfo font = fonts[0]; 325 assertNotNull(font.getUri()); 326 assertEquals(400, font.getWeight()); 327 assertNull(font.getAxes()); 328 assertFalse(font.isItalic()); 329 assertEquals(Columns.RESULT_CODE_OK, font.getResultCode()); 330 assertNotNull(FontsContract.buildTypeface(mContext, null /* cancellation signal */, fonts)); 331 } 332 333 @Test restrictContextRejection()334 public void restrictContextRejection() throws NameNotFoundException { 335 Context restrictedContext = mContext.createPackageContext( 336 PACKAGE, Context.CONTEXT_RESTRICTED); 337 338 FontRequest request = new FontRequest(AUTHORITY, PACKAGE, 339 MockFontProvider.SINGLE_FONT_FAMILY_QUERY, SIGNATURE); 340 341 // Rejected if restricted context is used. 342 FontFamilyResult result = FontsContract.fetchFonts( 343 restrictedContext, null /* cancellation signal */, request); 344 assertEquals(FontFamilyResult.STATUS_REJECTED, result.getStatusCode()); 345 346 // Even if you have a result, buildTypeface should fail with restricted context. 347 result = FontsContract.fetchFonts(mContext, null /* cancellation signal */, request); 348 assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); 349 assertNull(FontsContract.buildTypeface( 350 restrictedContext, null /* cancellation signal */, result.getFonts())); 351 } 352 } 353