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