1 /*
2  * Copyright (C) 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 android.platform.test.ravenwood;
18 
19 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
20 
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.function.Predicate;
26 
27 public class RavenwoodSystemProperties {
28     private volatile boolean mIsImmutable;
29 
30     private final Map<String, String> mValues = new HashMap<>();
31 
32     /** Set of additional keys that should be considered readable */
33     private final Set<String> mKeyReadable = new HashSet<>();
34     private final Predicate<String> mKeyReadablePredicate = (key) -> {
35         final String root = getKeyRoot(key);
36 
37         if (root.startsWith("debug.")) return true;
38 
39         // This set is carefully curated to help identify situations where a test may
40         // accidentally depend on a default value of an obscure property whose owner hasn't
41         // decided how Ravenwood should behave.
42         if (root.startsWith("boot.")) return true;
43         if (root.startsWith("build.")) return true;
44         if (root.startsWith("product.")) return true;
45         if (root.startsWith("soc.")) return true;
46         if (root.startsWith("system.")) return true;
47 
48         switch (key) {
49             case "gsm.version.baseband":
50             case "no.such.thing":
51             case "ro.bootloader":
52             case "ro.debuggable":
53             case "ro.hardware":
54             case "ro.hw_timeout_multiplier":
55             case "ro.odm.build.media_performance_class":
56             case "ro.treble.enabled":
57             case "ro.vndk.version":
58                 return true;
59         }
60 
61         return mKeyReadable.contains(key);
62     };
63 
64     /** Set of additional keys that should be considered writable */
65     private final Set<String> mKeyWritable = new HashSet<>();
66     private final Predicate<String> mKeyWritablePredicate = (key) -> {
67         final String root = getKeyRoot(key);
68 
69         if (root.startsWith("debug.")) return true;
70 
71         return mKeyWritable.contains(key);
72     };
73 
RavenwoodSystemProperties()74     public RavenwoodSystemProperties() {
75         // TODO: load these values from build.prop generated files
76         setValueForPartitions("product.brand", "Android");
77         setValueForPartitions("product.device", "Ravenwood");
78         setValueForPartitions("product.manufacturer", "Android");
79         setValueForPartitions("product.model", "Ravenwood");
80         setValueForPartitions("product.name", "Ravenwood");
81 
82         setValueForPartitions("product.cpu.abilist", "x86_64");
83         setValueForPartitions("product.cpu.abilist32", "");
84         setValueForPartitions("product.cpu.abilist64", "x86_64");
85 
86         setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024");
87         setValueForPartitions("build.date.utc", "1704092400");
88         setValueForPartitions("build.id", "MAIN");
89         setValueForPartitions("build.tags", "dev-keys");
90         setValueForPartitions("build.type", "userdebug");
91         setValueForPartitions("build.version.all_codenames", "REL");
92         setValueForPartitions("build.version.codename", "REL");
93         setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101");
94         setValueForPartitions("build.version.known_codenames", "REL");
95         setValueForPartitions("build.version.release", "14");
96         setValueForPartitions("build.version.release_or_codename", "VanillaIceCream");
97         setValueForPartitions("build.version.sdk", "34");
98 
99         setValue("ro.board.first_api_level", "1");
100         setValue("ro.product.first_api_level", "1");
101 
102         setValue("ro.soc.manufacturer", "Android");
103         setValue("ro.soc.model", "Ravenwood");
104 
105         setValue("ro.debuggable", "1");
106 
107         setValue(RAVENWOOD_SYSPROP, "1");
108     }
109 
110     /** Copy constructor */
RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable)111     public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
112         this.mKeyReadable.addAll(source.mKeyReadable);
113         this.mKeyWritable.addAll(source.mKeyWritable);
114         this.mValues.putAll(source.mValues);
115         this.mIsImmutable = immutable;
116     }
117 
getValues()118     public Map<String, String> getValues() {
119         return new HashMap<>(mValues);
120     }
121 
getKeyReadablePredicate()122     public Predicate<String> getKeyReadablePredicate() {
123         return mKeyReadablePredicate;
124     }
125 
getKeyWritablePredicate()126     public Predicate<String> getKeyWritablePredicate() {
127         return mKeyWritablePredicate;
128     }
129 
130     private static final String[] PARTITIONS = {
131             "bootimage",
132             "odm",
133             "product",
134             "system",
135             "system_ext",
136             "vendor",
137             "vendor_dlkm",
138     };
139 
ensureNotImmutable()140     private void ensureNotImmutable() {
141         if (mIsImmutable) {
142             throw new RuntimeException("Unable to update immutable instance");
143         }
144     }
145 
146     /**
147      * Set the given property for all possible partitions where it could be defined. For
148      * example, the value of {@code ro.build.type} is typically also mirrored under
149      * {@code ro.system.build.type}, etc.
150      */
setValueForPartitions(String key, String value)151     private void setValueForPartitions(String key, String value) {
152         ensureNotImmutable();
153 
154         setValue("ro." + key, value);
155         for (String partition : PARTITIONS) {
156             setValue("ro." + partition + "." + key, value);
157         }
158     }
159 
setValue(String key, Object value)160     public void setValue(String key, Object value) {
161         ensureNotImmutable();
162 
163         final String valueString = (value == null) ? null : String.valueOf(value);
164         if ((valueString == null) || valueString.isEmpty()) {
165             mValues.remove(key);
166         } else {
167             mValues.put(key, valueString);
168         }
169     }
170 
setAccessNone(String key)171     public void setAccessNone(String key) {
172         ensureNotImmutable();
173         mKeyReadable.remove(key);
174         mKeyWritable.remove(key);
175     }
176 
setAccessReadOnly(String key)177     public void setAccessReadOnly(String key) {
178         ensureNotImmutable();
179         mKeyReadable.add(key);
180         mKeyWritable.remove(key);
181     }
182 
setAccessReadWrite(String key)183     public void setAccessReadWrite(String key) {
184         ensureNotImmutable();
185         mKeyReadable.add(key);
186         mKeyWritable.add(key);
187     }
188 
189     /**
190      * Return the "root" of the given property key, stripping away any modifier prefix such as
191      * {@code ro.} or {@code persist.}.
192      */
getKeyRoot(String key)193     private static String getKeyRoot(String key) {
194         if (key.startsWith("ro.")) {
195             return key.substring(3);
196         } else if (key.startsWith("persist.")) {
197             return key.substring(8);
198         } else {
199             return key;
200         }
201     }
202 
203     /**
204      * Return an immutable, default instance.
205      */
206     // Create a default instance, and make an immutable copy of it.
207     public static final RavenwoodSystemProperties DEFAULT_VALUES =
208             new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true);
209 }