1 /*
<lambda>null2 * Copyright 2022 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 androidx.compose.ui.text.googlefonts
18
19 import android.annotation.SuppressLint
20 import android.content.pm.PackageInfo
21 import android.content.pm.PackageManager
22 import android.content.pm.Signature
23 import android.content.res.Resources
24 import androidx.annotation.WorkerThread
25 import androidx.core.content.res.FontResourcesParserCompat
26 import java.util.Arrays
27
28 @SuppressLint("ListIterator") // this is not a hot code path, nor is it optimized
29 @WorkerThread
30 internal fun GoogleFont.Provider.checkAvailable(
31 packageManager: PackageManager,
32 resources: Resources
33 ): Boolean {
34 // check package is available (false return paths)
35 @Suppress("DEPRECATION")
36 val providerInfo = packageManager.resolveContentProvider(providerAuthority, 0) ?: return false
37 if (providerInfo.packageName != providerPackage) return false
38
39 // now check signatures (true or except after this)
40 val signatures = packageManager.getSignatures(providerInfo.packageName)
41 val sortedSignatures = signatures.sortedWith(ByteArrayComparator)
42 val allExpectedCerts = loadCertsIfNeeded(resources)
43 val certsMatched =
44 allExpectedCerts.any { certList ->
45 val expected = certList?.sortedWith(ByteArrayComparator)
46 if (expected?.size != sortedSignatures.size) return@any false
47 for (i in expected.indices) {
48 if (!Arrays.equals(expected[i], sortedSignatures[i])) return@any false
49 }
50 true
51 }
52 return if (certsMatched) {
53 true
54 } else {
55 throwFormattedCertsMissError(signatures)
56 }
57 }
58
59 @SuppressLint("ListIterator") // not a hot code path, not optimized
throwFormattedCertsMissErrornull60 private fun throwFormattedCertsMissError(signatures: List<ByteArray>): Nothing {
61 val fullDescription =
62 signatures.joinToString(",", prefix = "listOf(listOf(", postfix = "))") { repr(it) }
63 throw IllegalStateException(
64 "Provided signatures did not match. Actual signatures of package are:\n\n$fullDescription"
65 )
66 }
67
reprnull68 private fun repr(b: ByteArray): String {
69 return b.joinToString(",", prefix = "byteArrayOf(", postfix = ")")
70 }
71
GoogleFontnull72 private fun GoogleFont.Provider.loadCertsIfNeeded(resources: Resources): List<List<ByteArray?>?> {
73 if (certificates != null) {
74 return certificates
75 }
76
77 return FontResourcesParserCompat.readCerts(resources, certificatesRes)
78 }
79
PackageManagernull80 private fun PackageManager.getSignatures(packageName: String): List<ByteArray> {
81 @Suppress("DEPRECATION")
82 @SuppressLint("PackageManagerGetSignatures")
83 val packageInfo: PackageInfo = getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
84 @Suppress("DEPRECATION") return convertToByteArrayList(packageInfo.signatures!!)
85 }
86
rnull87 private val ByteArrayComparator = Comparator { l: ByteArray, r: ByteArray ->
88 if (l.size != r.size) {
89 return@Comparator l.size - r.size
90 }
91 var i = 0
92 while (i < l.size) {
93 if (l[i] != r[i]) {
94 return@Comparator l[i] - r[i]
95 }
96 ++i
97 }
98 0
99 }
100
convertToByteArrayListnull101 private fun convertToByteArrayList(signatures: Array<Signature>): List<ByteArray> {
102 val shaList: MutableList<ByteArray> = ArrayList()
103 for (signature in signatures) {
104 shaList.add(signature.toByteArray())
105 }
106 return shaList
107 }
108