Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 2 | # |
| 3 | # Copyright (C) 2009 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 17 | import argparse |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 18 | import sys |
| 19 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 20 | # Usage: post_process_props.py file.prop [disallowed_key, ...] |
| 21 | # Disallowed keys are removed from the property file, if present |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 22 | |
Elliott Hughes | 05c1a2a | 2017-02-28 10:04:23 -0800 | [diff] [blame] | 23 | # See PROP_VALUE_MAX in system_properties.h. |
| 24 | # The constant in system_properties.h includes the terminating NUL, |
| 25 | # so we decrease the value by 1 here. |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 26 | PROP_VALUE_MAX = 91 |
| 27 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 28 | # Put the modifications that you need to make into the */build.prop into this |
| 29 | # function. |
| 30 | def mangle_build_prop(prop_list): |
Jerry Zhang | 1695653 | 2016-10-18 00:01:27 +0000 | [diff] [blame] | 31 | # If ro.debuggable is 1, then enable adb on USB by default |
| 32 | # (this is for userdebug builds) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 33 | if prop_list.get_value("ro.debuggable") == "1": |
| 34 | val = prop_list.get_value("persist.sys.usb.config") |
Jerry Zhang | 1695653 | 2016-10-18 00:01:27 +0000 | [diff] [blame] | 35 | if "adb" not in val: |
| 36 | if val == "": |
| 37 | val = "adb" |
| 38 | else: |
| 39 | val = val + ",adb" |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 40 | prop_list.put("persist.sys.usb.config", val) |
Joe Onorato | 8ad4bb1 | 2012-05-02 14:36:57 -0700 | [diff] [blame] | 41 | # UsbDeviceManager expects a value here. If it doesn't get it, it will |
| 42 | # default to "adb". That might not the right policy there, but it's better |
| 43 | # to be explicit. |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 44 | if not prop_list.get_value("persist.sys.usb.config"): |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame^] | 45 | prop_list.put("persist.sys.usb.config", "none") |
| 46 | |
| 47 | def validate_and_add_grf_props(prop_list, sdk_version): |
| 48 | """Validate GRF properties if exist. |
| 49 | |
| 50 | If ro.board.first_api_level is defined, check if its value is valid for the |
| 51 | sdk version. |
| 52 | Also, validate the value of ro.board.api_level if defined. If the |
| 53 | ro.board.api_level property is not defined, define it with the required |
| 54 | vendor API level for the GRF policy. |
| 55 | |
| 56 | Returns: |
| 57 | True if the GRF properties are valid. |
| 58 | """ |
| 59 | grf_api_level = prop_list.get_value("ro.board.first_api_level") |
| 60 | board_api_level = prop_list.get_value("ro.board.api_level") |
| 61 | |
| 62 | if not grf_api_level: |
| 63 | if board_api_level: |
| 64 | sys.stderr.write("error: non-GRF device must not define " |
| 65 | "ro.board.api_level\n") |
| 66 | return False |
| 67 | # non-GRF device skips the GRF validation test |
| 68 | return True |
| 69 | |
| 70 | grf_api_level = int(grf_api_level) |
| 71 | if grf_api_level > sdk_version: |
| 72 | sys.stderr.write("error: ro.board.first_api_level(%d) must be less than " |
| 73 | "or equal to ro.build.version.sdk(%d)\n" |
| 74 | % (grf_api_level, sdk_version)) |
| 75 | return False |
| 76 | |
| 77 | grf_window = 4 |
| 78 | grf_required_api_level = (grf_api_level |
| 79 | + grf_window * ((sdk_version - grf_api_level) // grf_window)) |
| 80 | |
| 81 | if board_api_level: |
| 82 | board_api_level = int(board_api_level) |
| 83 | if board_api_level < grf_api_level or board_api_level > sdk_version: |
| 84 | sys.stderr.write("error: ro.board.api_level(%d) must be neither less " |
| 85 | "than ro.board.first_api_level(%d) nor greater than " |
| 86 | "ro.build.version.sdk(%d)\n" |
| 87 | % (board_api_level, grf_api_level, sdk_version)) |
| 88 | return False |
| 89 | if board_api_level < grf_required_api_level: |
| 90 | sys.stderr.write("error: ro.board.api_level(%d) must be greater than or " |
| 91 | "equal to %d based on GRF policy\n" |
| 92 | % (board_api_level, grf_required_api_level)) |
| 93 | return False |
| 94 | else: |
| 95 | prop_list.put("ro.board.api_level", str(grf_required_api_level)) |
| 96 | |
| 97 | return True |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 98 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 99 | def validate(prop_list): |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 100 | """Validate the properties. |
| 101 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 102 | If the value of a sysprop exceeds the max limit (91), it's an error, unless |
| 103 | the sysprop is a read-only one. |
| 104 | |
| 105 | Checks if there is no optional prop assignments. |
| 106 | |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 107 | Returns: |
| 108 | True if nothing is wrong. |
| 109 | """ |
| 110 | check_pass = True |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 111 | for p in prop_list.get_all_props(): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 112 | if len(p.value) > PROP_VALUE_MAX and not p.name.startswith("ro."): |
Ying Wang | 38df101 | 2015-02-04 15:10:59 -0800 | [diff] [blame] | 113 | check_pass = False |
| 114 | sys.stderr.write("error: %s cannot exceed %d bytes: " % |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 115 | (p.name, PROP_VALUE_MAX)) |
| 116 | sys.stderr.write("%s (%d)\n" % (p.value, len(p.value))) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 117 | |
| 118 | if p.is_optional(): |
| 119 | check_pass = False |
| 120 | sys.stderr.write("error: found unresolved optional prop assignment:\n") |
| 121 | sys.stderr.write(str(p) + "\n") |
| 122 | |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 123 | return check_pass |
| 124 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 125 | def override_optional_props(prop_list, allow_dup=False): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 126 | """Override a?=b with a=c, if the latter exists |
| 127 | |
| 128 | Overriding is done by deleting a?=b |
| 129 | When there are a?=b and a?=c, then only the last one survives |
| 130 | When there are a=b and a=c, then it's an error. |
| 131 | |
| 132 | Returns: |
| 133 | True if the override was successful |
| 134 | """ |
| 135 | success = True |
| 136 | for name in prop_list.get_all_names(): |
| 137 | props = prop_list.get_props(name) |
| 138 | optional_props = [p for p in props if p.is_optional()] |
| 139 | overriding_props = [p for p in props if not p.is_optional()] |
| 140 | if len(overriding_props) > 1: |
| 141 | # duplicated props are allowed when the all have the same value |
| 142 | if all(overriding_props[0].value == p.value for p in overriding_props): |
Jiyong Park | 24d9cad | 2020-06-30 11:41:23 +0900 | [diff] [blame] | 143 | for p in optional_props: |
| 144 | p.delete("overridden by %s" % str(overriding_props[0])) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 145 | continue |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 146 | # or if dup is explicitly allowed for compat reason |
| 147 | if allow_dup: |
| 148 | # this could left one or more optional props unresolved. |
| 149 | # Convert them into non-optional because init doesn't understand ?= |
| 150 | # syntax |
| 151 | for p in optional_props: |
| 152 | p.optional = False |
| 153 | continue |
| 154 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 155 | success = False |
| 156 | sys.stderr.write("error: found duplicate sysprop assignments:\n") |
| 157 | for p in overriding_props: |
| 158 | sys.stderr.write("%s\n" % str(p)) |
| 159 | elif len(overriding_props) == 1: |
| 160 | for p in optional_props: |
| 161 | p.delete("overridden by %s" % str(overriding_props[0])) |
| 162 | else: |
| 163 | if len(optional_props) > 1: |
| 164 | for p in optional_props[:-1]: |
| 165 | p.delete("overridden by %s" % str(optional_props[-1])) |
| 166 | # Make the last optional one as non-optional |
| 167 | optional_props[-1].optional = False |
| 168 | |
| 169 | return success |
| 170 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 171 | class Prop: |
Yu Liu | 115c66b | 2014-02-10 19:20:36 -0800 | [diff] [blame] | 172 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 173 | def __init__(self, name, value, optional=False, comment=None): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 174 | self.name = name.strip() |
| 175 | self.value = value.strip() |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 176 | if comment != None: |
| 177 | self.comments = [comment] |
| 178 | else: |
| 179 | self.comments = [] |
| 180 | self.optional = optional |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 181 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 182 | @staticmethod |
| 183 | def from_line(line): |
| 184 | line = line.rstrip('\n') |
| 185 | if line.startswith("#"): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 186 | return Prop("", "", comment=line) |
| 187 | elif "?=" in line: |
| 188 | name, value = line.split("?=", 1) |
| 189 | return Prop(name, value, optional=True) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 190 | elif "=" in line: |
| 191 | name, value = line.split("=", 1) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 192 | return Prop(name, value, optional=False) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 193 | else: |
| 194 | # don't fail on invalid line |
| 195 | # TODO(jiyong) make this a hard error |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 196 | return Prop("", "", comment=line) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 197 | |
| 198 | def is_comment(self): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 199 | return bool(self.comments and not self.name) |
| 200 | |
| 201 | def is_optional(self): |
| 202 | return (not self.is_comment()) and self.optional |
| 203 | |
| 204 | def make_as_comment(self): |
| 205 | # Prepend "#" to the last line which is the prop assignment |
| 206 | if not self.is_comment(): |
| 207 | assignment = str(self).rsplit("\n", 1)[-1] |
| 208 | self.comments.append("#" + assignment) |
| 209 | self.name = "" |
| 210 | self.value = "" |
| 211 | |
| 212 | def delete(self, reason): |
| 213 | self.comments.append("# Removed by post_process_props.py because " + reason) |
| 214 | self.make_as_comment() |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 215 | |
| 216 | def __str__(self): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 217 | assignment = [] |
| 218 | if not self.is_comment(): |
| 219 | operator = "?=" if self.is_optional() else "=" |
| 220 | assignment.append(self.name + operator + self.value) |
| 221 | return "\n".join(self.comments + assignment) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 222 | |
| 223 | class PropList: |
| 224 | |
| 225 | def __init__(self, filename): |
| 226 | with open(filename) as f: |
| 227 | self.props = [Prop.from_line(l) |
| 228 | for l in f.readlines() if l.strip() != ""] |
| 229 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 230 | def get_all_props(self): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 231 | return [p for p in self.props if not p.is_comment()] |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 232 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 233 | def get_all_names(self): |
| 234 | return set([p.name for p in self.get_all_props()]) |
| 235 | |
| 236 | def get_props(self, name): |
| 237 | return [p for p in self.get_all_props() if p.name == name] |
| 238 | |
| 239 | def get_value(self, name): |
| 240 | # Caution: only the value of the first sysprop having the name is returned. |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 241 | return next((p.value for p in self.props if p.name == name), "") |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 242 | |
| 243 | def put(self, name, value): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 244 | # Note: when there is an optional prop for the name, its value isn't changed. |
| 245 | # Instead a new non-optional prop is appended, which will override the |
| 246 | # optional prop. Otherwise, the new value might be overridden by an existing |
| 247 | # non-optional prop of the same name. |
| 248 | index = next((i for i,p in enumerate(self.props) |
| 249 | if p.name == name and not p.is_optional()), -1) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 250 | if index == -1: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 251 | self.props.append(Prop(name, value, |
| 252 | comment="# Auto-added by post_process_props.py")) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 253 | else: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 254 | self.props[index].comments.append( |
| 255 | "# Value overridden by post_process_props.py. Original value: %s" % |
| 256 | self.props[index].value) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 257 | self.props[index].value = value |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 258 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 259 | def write(self, filename): |
| 260 | with open(filename, 'w+') as f: |
| 261 | for p in self.props: |
| 262 | f.write(str(p) + "\n") |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 263 | |
| 264 | def main(argv): |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 265 | parser = argparse.ArgumentParser(description="Post-process build.prop file") |
| 266 | parser.add_argument("--allow-dup", dest="allow_dup", action="store_true", |
| 267 | default=False) |
| 268 | parser.add_argument("filename") |
| 269 | parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*") |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame^] | 270 | parser.add_argument("--sdk-version", type=int, required=True) |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 271 | args = parser.parse_args() |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 272 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 273 | if not args.filename.endswith("/build.prop"): |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 274 | sys.stderr.write("bad command line: " + str(argv) + "\n") |
| 275 | sys.exit(1) |
| 276 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 277 | props = PropList(args.filename) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 278 | mangle_build_prop(props) |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 279 | if not override_optional_props(props, args.allow_dup): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 280 | sys.exit(1) |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame^] | 281 | if not validate_and_add_grf_props(props, args.sdk_version): |
| 282 | sys.exit(1) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 283 | if not validate(props): |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 284 | sys.exit(1) |
| 285 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 286 | # Drop any disallowed keys |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 287 | for key in args.disallowed_keys: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 288 | for p in props.get_props(key): |
| 289 | p.delete("%s is a disallowed key" % key) |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 290 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 291 | props.write(args.filename) |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 292 | |
| 293 | if __name__ == "__main__": |
| 294 | main(sys.argv) |