1 /*
2  * Copyright 2024 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.core.backported.fixes
18 
19 import java.util.BitSet
20 
21 internal const val ALIAS_BITSET_PROP_NAME = "ro.build.backported_fixes.alias_bitset.long_list"
22 
23 /**
24  * Resolves the status of a [KnownIssue] using `ro.build.backported_fixes.alias_bitset.long_list`
25  * system property.
26  */
27 internal class SystemPropertyResolver : StatusResolver {
28 
<lambda>null29     val aliases: Set<Int> by lazy { initAliases() }
30 
invokenull31     override fun invoke(ki: KnownIssue): Status {
32         return if (aliases.contains(ki.alias)) {
33             Status.Fixed
34         } else {
35             // Because only Approved issues are included in the enum aliases
36             // if the alias is not found return "NOT FIXED"
37             Status.NotFixed
38         }
39     }
40 
initAliasesnull41     private fun initAliases(): Set<Int> {
42         // java.util.BitSet are not thread safe, so extract the aliases here.
43         val bs = BitSet.valueOf(parseLongListString(getAliasBitsetString()))
44         // bs.stream is not available until SDK 23 so extract aliases by hand.
45         val size = bs.size()
46         if (size == 0) {
47             return emptySet()
48         }
49         val result =
50             buildSet(size) {
51                 var next = 0
52                 while (next >= 0) {
53                     if (bs.get(next)) {
54                         add(next)
55                     }
56                     if (next == Integer.MAX_VALUE) {
57                         break
58                     }
59                     next = bs.nextSetBit(next + 1)
60                 }
61             }
62         return result
63     }
64 
parseLongListStringnull65     private fun parseLongListString(s: String): LongArray {
66         val list = buildList {
67             for (x in s.split(',')) {
68                 try {
69                     val l = x.toLong()
70                     add(l)
71                 } catch (e: NumberFormatException) {
72                     // Since the order matters, stop and just return what we have.
73                     break
74                 }
75             }
76         }
77         return list.toLongArray()
78     }
79 
getAliasBitsetStringnull80     private fun getAliasBitsetString(): String {
81         // TODO b/381267367 - add sdk check Build.getBackportedFixStatus in when available.
82         try {
83             val c = Class.forName("android.os.SystemProperties")
84             val get = c.getMethod("get", String::class.java, String::class.java)
85 
86             return get.invoke(c, ALIAS_BITSET_PROP_NAME, "") as String
87         } catch (e: Exception) {
88             return ""
89         }
90     }
91 }
92